Data Transfer
How pieces are served under payment check constraints during a paid session.
After a payment session is established via verification, the Seeder begins serving pieces to the Leecher while tracking payment checks. This phase reuses the normal BitTorrent request/piece/cancel messages and adds only local accounting.
Mapping Pieces to Cost
SeedPay doesn't modify BitTorrent wire messages for data transfer. Instead, the Seeder observes each request message and converts the byte length into monetary cost:
cost = bytes / (1024 × 1024) × price_per_mbThe Seeder maintains per payment session:
| Field | Description |
|---|---|
channel_id | Payment channel identifier |
channel_deposit | Total amount locked in escrow |
last_check_nonce | Highest nonce from valid payment checks |
last_check_amount | Highest amount authorized by valid checks |
bytes_downloaded | Cumulative bytes served |
price_per_mb | Agreed rate from handshake |
Tracking on successful piece send is RECOMMENDED for accuracy.
Serving Requests
For each request(index, begin, length) from the Leecher, the Seeder:
- Looks up the payment session for this connection
- Computes the cost of serving this block
- Computes cumulative cost:
(bytes_downloaded + length) / (1024 × 1024) × price_per_mb - Checks whether
last_check_amountcovers thecumulative_cost
If Payment is Sufficient
The Seeder increments bytes_downloaded and sends the piece message as normal.
If Payment is Insufficient
The Seeder MAY send a payment_check_required message:
{
"type": "payment_check_required",
"required_amount": 0.005,
"current_check_amount": 0.003,
"estimated_remaining_mb": 20.0
}Before choking, the Seeder SHOULD wait a short grace period (e.g. 5 seconds) to allow in-flight payment checks to arrive. This prevents unnecessary choke/unchoke cycles.
After the grace period, if no valid check has been received, the Seeder chokes the Leecher until a new payment check arrives.
Payment Check Processing
When the Seeder receives a payment_check during data transfer:
- Verify the signature using the Leecher's public key from channel state
- Verify nonce is greater than
last_check_nonce(prevents replay) - Verify amount is greater than or equal to
last_check_amount(monotonically increasing) - Verify amount does not exceed
channel_deposit
If all validations pass:
- Update
last_check_nonceandlast_check_amount - Unchoke the Leecher if previously choked
- Continue serving pieces
If validation fails:
- Reject the check and keep the Leecher choked
- MAY send a
payment_check_rejectederror (see Message Types)
Session Lifetime
A Seeder SHOULD treat a session as expired if:
- The channel timeout has been reached
- The connection is closed or idle for too long
- The ephemeral keys have been deleted
When a session ends, the Seeder SHOULD:
- Submit the highest valid payment check to close the channel cooperatively
- Discard session state (channel tracking, usage counters)
- Delete the ephemeral secret key (ensures forward secrecy)
The Seeder MAY continue to serve as a free seeder (if local policy allows) or close the connection entirely.