Skip to content

HIP-150: accept data transfer multiplier tickets in ingest - #1242

Merged
michaeldjeffrey merged 2 commits into
mj/hip-150from
mj/hip-150-ingest-tickets
Aug 26, 2026
Merged

HIP-150: accept data transfer multiplier tickets in ingest#1242
michaeldjeffrey merged 2 commits into
mj/hip-150from
mj/hip-150-ingest-tickets

Conversation

@michaeldjeffrey

Copy link
Copy Markdown
Contributor

Adds submit_data_transfer_multiplier_ticket. A ticket grants one hotspot a multiplier on the data credits derived from its rewardable bytes.

Ingest verifies the signer and the timestamp, then persists the ticket verbatim. It does not parse the multiplier — the packet verifier decides whether one is acceptable and records that verdict in a verified report, so a rejection stays as auditable as a grant.

Notes:

  • Ticket signers get their own allow-list and is_ticket_signer() check, rather than a NetworkKeyRole variant: that enum belongs to the decommissioned mobile-config. A carrier key must not be able to grant a multiplier.
  • The list may be empty and defaults to empty, since the release ships before any ticket can be issued. Empty rejects every ticket and warns at startup.
  • Tickets older than data_transfer_multiplier_ticket_max_age (default 10 minutes) or dated in the future are refused. Signatures never expire, so without this a captured ticket is replayable forever.
  • valid_data_transfer_session gained a multiplier field with the proto bump; set to None here, so behaviour is unchanged.

Cargo.toml patches helium-proto to proto's mj/hip-150 (helium/proto#483). REVERT BEFORE MERGING mj/hip-150 TO main.

Adds submit_data_transfer_multiplier_ticket. A ticket grants one hotspot a
multiplier on the data credits derived from its rewardable bytes.

Ingest verifies the signer and the timestamp, then persists the ticket
verbatim. It does not parse the multiplier — the packet verifier decides
whether one is acceptable and records that verdict in a verified report, so
a rejection stays as auditable as a grant.

Notes:

- Ticket signers get their own allow-list and is_ticket_signer() check,
  rather than a NetworkKeyRole variant: that enum belongs to the
  decommissioned mobile-config. A carrier key must not be able to grant a
  multiplier.
- The list may be empty and defaults to empty, since the release ships
  before any ticket can be issued. Empty rejects every ticket and warns at
  startup.
- Tickets older than data_transfer_multiplier_ticket_max_age (default 10
  minutes) or dated in the future are refused. Signatures never expire, so
  without this a captured ticket is replayable forever.
- valid_data_transfer_session gained a multiplier field with the proto
  bump; set to None here, so behaviour is unchanged.

Cargo.toml patches helium-proto to proto's mj/hip-150 (helium/proto#483).
REVERT BEFORE MERGING mj/hip-150 TO main.
A client does not share a clock with ingest, so a ticket signed at what the
client believes is "now" can arrive stamped slightly ahead of us. Those were
refused as post-dated, which is a confusing failure for an honest client
with a drifting clock.

Tickets up to MAX_CLOCK_DRIFT (1 minute) in the future are now treated as
current. Beyond that they are still refused: post-dating must not buy an
attacker a longer replay window than an honest client gets, and a ticket
inside the allowance still ages out of the freshness window at the same
rate, it just starts a minute earlier.

The allowance is a shared constant in file-store-oracles rather than a
setting in each service. The packet verifier checks freshness too, and
measures a ticket's age against the timestamp ingest stamped on it — so if
ingest tolerated drift the verifier did not, every ticket ingest accepted
from a fast client would be refused downstream. One value, not two that can
be configured apart.
@michaeldjeffrey
michaeldjeffrey merged commit ecd95d5 into mj/hip-150 Aug 26, 2026
@michaeldjeffrey
michaeldjeffrey deleted the mj/hip-150-ingest-tickets branch August 26, 2026 17:29
michaeldjeffrey added a commit that referenced this pull request Aug 28, 2026
* HIP-150: accept data transfer multiplier tickets in ingest

Adds submit_data_transfer_multiplier_ticket. A ticket grants one hotspot a
multiplier on the data credits derived from its rewardable bytes.

Ingest verifies the signer and the timestamp, then persists the ticket
verbatim. It does not parse the multiplier — the packet verifier decides
whether one is acceptable and records that verdict in a verified report, so
a rejection stays as auditable as a grant.

Notes:

- Ticket signers get their own allow-list and is_ticket_signer() check,
  rather than a NetworkKeyRole variant: that enum belongs to the
  decommissioned mobile-config. A carrier key must not be able to grant a
  multiplier.
- The list may be empty and defaults to empty, since the release ships
  before any ticket can be issued. Empty rejects every ticket and warns at
  startup.
- Tickets older than data_transfer_multiplier_ticket_max_age (default 10
  minutes) or dated in the future are refused. Signatures never expire, so
  without this a captured ticket is replayable forever.
- valid_data_transfer_session gained a multiplier field with the proto
  bump; set to None here, so behaviour is unchanged.

Cargo.toml patches helium-proto to proto's mj/hip-150 (helium/proto#483).
REVERT BEFORE MERGING mj/hip-150 TO main.

* HIP-150: tolerate client clock drift on multiplier tickets

A client does not share a clock with ingest, so a ticket signed at what the
client believes is "now" can arrive stamped slightly ahead of us. Those were
refused as post-dated, which is a confusing failure for an honest client
with a drifting clock.

Tickets up to MAX_CLOCK_DRIFT (1 minute) in the future are now treated as
current. Beyond that they are still refused: post-dating must not buy an
attacker a longer replay window than an honest client gets, and a ticket
inside the allowance still ages out of the freshness window at the same
rate, it just starts a minute earlier.

The allowance is a shared constant in file-store-oracles rather than a
setting in each service. The packet verifier checks freshness too, and
measures a ticket's age against the timestamp ingest stamped on it — so if
ingest tolerated drift the verifier did not, every ticket ingest accepted
from a fast client would be refused downstream. One value, not two that can
be configured apart.
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