open-standards/wai/extensions/revenue-share.md
Transaction Science ba9c439cf9 receipt-ledger §5.2: one registry of record kinds; the job kinds are 14 to 16
Kinds 10 to 13 were already revenue-share §7.4's run and invoice and
opportunity-report §7.3's cue event and room policy; the job source's OER/2
receipt, int_motion decode receipt and route record took 10 to 12 and so meant
two things. They are now 14, 15 and 16. §5.2's table registers every kind a
WAI text names a record by, 10 to 13 pointing to the texts that define them,
unchanged, and those texts say so. Kinds 128 to 255 are reserved for private
and provisional use: a WAI record never states one, and an invoice that lists
one is refused private_kind, in either version. A version-2 invoice lists kinds
1 to 9 and 14 to 16, never another text's. Three controls; the corpus and both
implementations follow.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 00:51:06 -04:00

605 lines
37 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# WAI Extension: Revenue Share
> Status: Draft. Registers the record `wai.delivery.revenue_share`: what each content owner of a
> linear channel is owed from the ads sold in the channel's breaks, as a statement anyone holding
> the public records recomputes. An operator and every owner it names sign a **share contract**,
> `WRC1`: its period, its channel and the key that signs the channel's program-as-run, the
> carriers whose ad evidence belongs to the channel, the map from each asset to a **split** of
> integer millionths among owners and the operator, and the rule for a break that falls between
> programmes. The operator signs a **statement**, `WRS1`, for one party and one period: the
> invoices of [receipt-ledger](receipt-ledger.md) it rests on, the program-as-run windows of
> `wai.video.channel` it places impressions in, and one line per split and class, each with the
> impressions attributed, the revenue the invoices' tariffs priced them at, the party's share of
> it divided by the largest-remainder rule, and the records the line came from. A verifier given
> the contract, the windows and the ledgers' own verification documents recomputes every line and
> refuses a statement that does not recompute. No new transport, no payment and no currency
> conversion: a statement is the recomputable figures a payment to an owner rests on. The
> reference implementation is the `revenue_share` module of `wai-rs` (feature `revenue_share`);
> the corpus is [`live-conformance/share/`](../live-conformance/share/), and its second
> implementation is `live-conformance/revenue_share_verify.py`.
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
>
> Texts read for this revision: the WAI texts this one cites; ISO 4217 (the alphabetic currency
> code); RFC 3339 §5.6, for the date-time form of §5.4; RFC 8032; RFC 8259; FIPS 180-4.
>
> Why the name: the extension states how revenue is shared, and the record names both of its
> parts, the contract and the statement, under one string, as `wai.delivery.ledger` names its
> tariff and its invoice.
## 1. Scope
In scope: the share contract, its splits, its signatures and when two contracts overlap (§3); the
program-as-run as a verifier is given it, the JSON carriage of a `wai.video.channel` receipt, whose
signed bytes this text writes out and does not change (§4); the evidence, invoices of the receipt
ledger and the impressions their sources state, with their instants (§5); attribution, from an
impression's instant to an asset and its split (§6); the statement, its lines, rounding and
citations (§7); verification (§8); how a statement composes with the ledger (§9); and the
operations (§10).
Out of scope: a transport; paying an owner, an owner's account and any contact with a party
outside the records; invoicing a buyer, which the receipt ledger does; converting a currency; how
an operator chooses an owner's share; placing in time a presentation whose marker states no time
this text reads (§5.4), which is kept in a class of its own; and the records themselves, which
their own extensions define, verify and leave as they are. The channel receipt keeps its signed
bytes: §4 writes them out for a verifier that is not `wai-rs`.
Relationship to the core spec: `wai.delivery.revenue_share` is a **record** ([SPEC](../SPEC.md)
§5): a signed record a WAI tool emits and verifies. It names no payload format and is not
dispatchable.
## 2. Terms
- **Operator**, **owner**: Ed25519 keys (RFC 8032). The operator carries the channel, sells its
breaks and signs contracts and statements; an owner holds programmes the channel airs. A
**party** of a contract is its operator or one of its owners.
- **Time**: microseconds since 1970-01-01T00:00:00Z, the unit of receipt-ledger §2. A **period**
is a span of time from its `start` (included) to its `end` (excluded). A channel receipt states
milliseconds since the same instant; an instant of *m* milliseconds is *m* × 1000 microseconds,
computed without overflow.
- **Run**: one `wai.video.channel` receipt, a signed program-as-run window (§4). Its **window** is
`[window_start_ms, window_end_ms)`, and an **entry** is one slot of its schedule.
- **Programme**: an entry of kind `program`. An entry of kind `ad` or `filler` is not a programme.
- **Carrier**: what delivers the channel's ads: a room of [live-ads](live-ads.md), named by its
room id, whose break-form receipts the ledger bills; or a stitcher of
[stitched-presentation](stitched-presentation.md), named by the key that signs its pod-input
records.
- **Ledger document**: a verification document of receipt-ledger §7.1, for one invoice.
- **Impression**: one ad presented or billed, as the ledger counts it (§5.2).
- **Minor unit**, **amount**: as receipt-ledger §2: an amount is an integer of millionths of a
currency's minor unit.
- **ppm**: an integer share in millionths, of 10^6.
- *u64*, *u32*, *u16*, *u8*: little-endian unsigned integers of that many bits in the records of
§3 and §7; the channel receipt's signed bytes (§4.2) are big-endian, as `wai.video.channel`
writes them. A **digest** is SHA-256 (FIPS 180-4), 32 bytes; **zero** of a key or digest field
is 32 zero bytes. A *text* field is a u16 length and that many bytes of UTF-8.
## 3. The share contract
### 3.1 Layout
| field | size | meaning |
|---|---|---|
| `magic` | 4 | `WRC1` (ASCII) |
| `version` | 1 | 1 in this revision |
| `operator` | 32 | the operator's key, which signs the contract and every statement under it |
| `start`, `end` | 8 each | u64: the period the contract is in force |
| `currency` | 3 | three capitals `A`–`Z` (ISO 4217): the currency of every invoice it shares |
| `channel` | 2 + *n* | a text field of 1 to 1024 bytes: the `channel_id` of the channel's runs |
| `channel_signer` | 32 | the key that signs the channel's runs |
| `rule` | 1 | for an impression in an entry that is not a programme (§6.2): 1 **preceding**, 2 **following**, 3 **between** |
| `between` | 2 | u16: the index of the **between split** (§6.2, §6.3) |
| `carriers` | 2 + 33 × *c* | a u16 count, then each carrier: `kind` (u8: 1 a room, 2 a stitcher) and `id` (32: the room id, or the stitcher's key), in strictly ascending order of (`kind`, `id`) |
| `owners` | 1 + 32 × *o* | a u8 count, then each owner's key, in strictly ascending order |
| `splits` | 2 + … | a u16 count, then each split (§3.2) |
| `assets` | 4 + … | a u32 count, then each asset: `asset` (a text field of 1 to 1024 bytes, an `asset_id` of the channel's schedule) and `split` (u16, the index of its split), in strictly ascending order of `asset`'s bytes |
| `operator_sig` | 64 | §3.3 |
| `countersigs` | 64 × *o* | each owner's countersignature, in the order of `owners` (§3.3) |
### 3.2 Splits
A split divides revenue among parties:
| field | size | meaning |
|---|---|---|
| `operator_ppm` | 4 | u32: the operator's share, 0 to 10^6 |
| `m` | 1 | u8: the owners it names |
| `shares` | 5 × *m* | each: `owner` (u8, the owner's index in `owners`) and `ppm` (u32, its share), in strictly ascending order of `owner` |
The **parties** of a split, in their **order**, are its owners in the order it lists them, then
the operator when `operator_ppm` is above 0. A party **holds a share** of a split when it is one of
its parties. The shares of a split sum to 10^6 exactly: a split is the whole of the revenue it is
given, and no part of it is left unassigned.
### 3.3 Digest and signatures
The **body** of a contract is its bytes before `operator_sig`, and its **digest** is SHA-256 over
the body. `operator_sig` is Ed25519 by `operator` over the bytes `wai:share-contract` (ASCII) and
0x01, followed by the body. Each owner's countersignature is Ed25519 by that owner over the bytes
`wai:share-accept` (ASCII) and 0x01, followed by the body; 64 zero bytes in its place state that
the owner has not countersigned. An operator offers a contract with every countersignature zero,
and each owner fills its own. Every signature is verified strictly, as [SPEC](../SPEC.md) §7
*Signatures* requires: a key that fails step 1 or 2 is `bad_key`, and a signature that fails a
later step is `bad_signature`.
A contract is **in force** only when every owner it names has countersigned it: the splits and
the asset map bind each owner only by its own signature, and a contract some owner did not sign
shares nothing.
### 3.4 Reading and rules
A reader reads exactly the bytes it is given as one contract, in the order of §3.1, and refuses at
the first of these that holds: fewer than 4 bytes, `truncated`; a magic other than `WRC1`,
`malformed`; no fifth byte, `truncated`; a version other than 1, `unknown_version`; the bytes end
before a field the layout requires, `truncated`; bytes after the last countersignature,
`malformed`. It then refuses as `contract_invalid` a contract that breaks any of these:
- `start` is below `end`; `currency` is three capitals `A`–`Z`;
- `operator` and `channel_signer` are not zero, and `operator` is not an owner;
- `channel` and every `asset` are UTF-8 of 1 to 1024 bytes;
- `rule` is 1, 2 or 3, and `between` is the index of a split;
- `carriers` has at least one entry, each `kind` 1 or 2 and each `id` not zero, in strictly
ascending order;
- `owners` has at least one key, none zero, in strictly ascending order;
- `splits` has at least one split; in each, the owners are in strictly ascending order of index,
each index below the count of `owners`, and each `ppm` above 0; and every owner holds a share of
at least one split;
- `assets` has at least one entry, in strictly ascending order, each `split` the index of a split.
It refuses as `split_not_whole` a contract with a split whose `operator_ppm` and `ppm`s do not sum
to 10^6. Then it verifies `operator_sig` (`bad_key`, `bad_signature`), then each countersignature
in turn: one of 64 zero bytes is `not_countersigned`, and one that does not verify is `bad_key` or
`bad_signature`. A refusal of a signature names the operator, or the owner and its index.
### 3.5 Contracts that overlap
Two contracts **overlap** when their periods share an instant and they name the same `channel`, or
a carrier in common. An asset aired under two contracts at once would have two owners, and a
statement under either would share revenue the other also shares; a verifier holding both refuses
a statement under either (§8.2 step 2, `contract_overlap`). An operator **MUST NOT** sign two
contracts that overlap; one that changes a contract's terms signs a new contract whose period
begins where the old one's ends.
## 4. The program-as-run
### 4.1 Its JSON carriage
A run is carried as one JSON object (RFC 8259) with exactly these members; a byte field is
lowercase hexadecimal of its exact length, and an integer a JSON integer from 0 to 2^64 − 1:
| member | value |
|---|---|
| `channel_id` | a string: the channel |
| `schedule_root` | 32 bytes: §4.2 |
| `schedule` | an array of entries, each `{"asset_id", "start_ms", "duration_ms", "kind"}`: `asset_id` a string, `kind` one of `program`, `ad` and `filler` |
| `window_start_ms`, `window_end_ms` | the window |
| `aired` | an array of integers: the indices of the entries on air in the window |
| `work_schedule_ops` | the entries' count, as the receipt states it |
| `joules_micro` | the assembly's energy, or 0 unmetered |
| `energy_provenance` | its class's label (energy-measurement §2.7: `ModelBased`, `Estimator`, `OnChipCounter`, `CalibratedInstrument`), or `null` unmetered |
| `energy_uncertainty`, `energy_counter_filtering`, `energy_calibration_ref` | only for a class that carries them, as live-ads §8.2 writes them |
| `grant` | `{"grant_hash", "capability", "joule_ceiling_micro", "funds_ceiling"}`, `funds_ceiling` `null` for none |
| `parent_receipt_hash` | 32 bytes: the run before it in the channel's chain, or `null` |
| `signer_pubkey` | 32 bytes: the channel signer's key |
| `signer_id` | a string |
| `sig` | 64 bytes: §4.2 |
A run **reads** when it is such an object and each entry's `start_ms` + `duration_ms` is at most
2^64 − 1. The legacy class label `HwShunt` (energy-measurement §2.6) does not read in this
carriage.
### 4.2 Its signed bytes and identity
These are the bytes `wai.video.channel` signs, written out; BLAKE3 throughout, integers
big-endian:
- an entry's **leaf**: BLAKE3 over `wai:video-channel-entry` (ASCII) and 0x01, `asset_id`'s length
(u32) and bytes, `start_ms` (u64), `duration_ms` (u64) and its kind's tag (u8: 1 `program`, 2
`ad`, 3 `filler`);
- the **schedule root**: the Merkle root over the leaves in schedule order, [jwp-receipts](jwp-receipts.md)
§2.2's tree, each leaf taken as a content hash (its first leaf form) and the final one duplicated
on an odd level; all zero for an empty schedule;
- the **signing payload**: `wai:video-channel-receipt` (ASCII) and 0x01; `channel_id`'s length
(u32) and bytes; `schedule_root`; `window_start_ms`, `window_end_ms`, `work_schedule_ops` and
`joules_micro` (u64 each); the class in its signed encoding (energy-measurement §2.7), the tag
byte 0 for none; the grant reference as live-ads §8.4 writes it (`grant_hash`, `capability`'s
length (u32) and bytes, `joule_ceiling_micro` (u64), then 1 and `funds_ceiling` (u64), or 0);
1 and `parent_receipt_hash`, or 0; the count of `aired` (u64) and each index (u64); the count of
`schedule` (u64) and each entry's leaf; and `signer_pubkey`, then `signer_id`'s length (u32) and
bytes;
- `sig`: Ed25519 by `signer_pubkey` over the signing payload, verified strictly;
- the run's **identity**, its receipt hash: BLAKE3 over `wai:video-channel-receipt-id` (ASCII) and
0x01, the signing payload and `sig`.
### 4.3 When a run verifies
A run verifies when:
1. `schedule_root` is the schedule root of its entries, and `work_schedule_ops` their count;
2. `window_start_ms` is below `window_end_ms`;
3. the schedule is continuous: each entry starts where the one before it ends;
4. `aired` is exactly the indices, ascending, of the entries that touch the window: those whose
`start_ms` is below `window_end_ms` and whose `start_ms` + `duration_ms` is above
`window_start_ms`;
5. the figure and its class cohere as live-ads §8.5 rule 5 states it; and
6. its signature verifies.
So the program-as-run is the schedule the channel signer signed, and what aired in a window is
recomputed from it rather than read from a log.
## 5. The evidence
### 5.1 Invoices
A statement rests on **invoices** of the receipt ledger, each given as its ledger document
(receipt-ledger §7.1): the tariff, the invoice, the invoice before it in its chain, and the
sources. A ledger document is **accepted** when the receipt ledger's verification (§7.2 there)
accepts it. Its tariff states the prices, its invoice the period and the records, and its sources
the impressions. A statement prices nothing itself: an impression's revenue is the price the
invoice billed it at.
Each invoice of a statement is one the operator sold as the seller of its tariff, in the
contract's currency; the buyer is whoever bought the channel's breaks under that tariff. A channel
whose breaks several buyers bought rests on several invoices, one chain per tariff.
### 5.2 Impressions and their classes
An accepted ledger document contributes, from its sources in order:
- for an `ads` source whose `room` is a carrier of kind 1: each receipt in the source's order, and
each of its impressions in slot order that the ledger counts (receipt-ledger §6.3 and §8.1):
- under a policy that requires presentation claims, an impression that counts, its own claim
`presented` (ad-presentation §9.2): class **presented**, its records the receipt (kind 6) and
its own claim (kind 7);
- under a policy that does not, a billable impression (quartile at least 1): class **beacons**,
its record the receipt (kind 6);
- for a `stitched` source: each record the source accepts whose `signer` is a carrier of kind 2,
each of its ads in slot order, and each presentation of the ad (stitched-presentation §7.4, a
presented own claim) in the order its claims are given: class **presented** when its instant is
stated (§5.4) and **unplaced** otherwise, its records the pod-input record (kind 8) and the
claim (kind 9).
Record kinds are receipt-ledger §5.2's. Every other source, and every impression of a carrier the
contract does not name, contributes nothing: an invoice may bill a buyer for several channels, and
a statement shares the impressions of its own.
An impression's **price** is its invoice's tariff's `ads` price (receipt-ledger §4.1): per
presented impression for the classes presented and unplaced, the unit `presented_impressions`
bills; per billable impression for the class beacons, the unit `billable_impressions` bills. So an
impression is priced at the unit the invoice billed it under, and the classes keep apart what the
players' own claims show presented and what rests on a decider's beacons alone. A class is never
summed into another: each has its own lines (§7.2).
### 5.3 Its instant
An impression's **instant** places it on the channel's timeline:
- an impression of an `ads` source: its room's `t0` plus the `t` of the break its receipt names
(live-ads §4.1, placed as receipt-ledger §5.3 places it). Every impression of a break shares the
break's instant: a break is attributed whole;
- a presentation of a stitched record: the instant its marker states (§5.4). A presentation whose
marker states none has no instant: it is **unplaced**.
### 5.4 The instant a marker states
A marker of carriage 1 or 2 (stitched-presentation §3.1) carries the `START-DATE` of its date
range, the second long field of its body. It **states** an instant when it is exactly this form, in
ASCII, of RFC 3339 §5.6's `date-time` with its letters in capitals:
```
YYYY "-" MM "-" DD "T" hh ":" mm ":" ss [ "." 1 to 9 digits ] ( "Z" / ( "+" / "-" ) hh ":" mm )
```
with `YYYY` a year of four digits, `MM` 01 to 12, `DD` 01 to the days of that month in the
Gregorian calendar, `hh` 00 to 23, `mm` 00 to 59 and `ss` 00 to 59 (an offset's `hh` and `mm` the
same), and the instant it names, in microseconds, at or after 1970-01-01T00:00:00Z. The instant is
the date and time less the offset, its fraction truncated to the microsecond. A marker of carriage
3 carries no date (§3.4 there): its presentations are unplaced, as are those of a `START-DATE` that
does not state an instant.
## 6. Attribution
### 6.1 The run of an instant
The runs a verifier is given are of the contract's channel and signer and do not overlap (§8.2
step 4). The **run** of an instant is the run whose window holds it; the **entry** of the instant
is the entry of that run's schedule whose span holds it. An impression with an instant and no run,
or a run and no entry (an instant past its schedule's end), aired in no window the verifier holds:
it is refused `outside_window`.
### 6.2 The asset and its split
The entry of an impression's instant gives its **asset**:
- a programme gives its own `asset_id`: a break inside a programme is that programme's;
- an entry that is not a programme gives, by the contract's `rule`: for **preceding**, the
`asset_id` of the last programme before it in the run's schedule; for **following**, that of the
first programme after it; when there is no such programme, or for **between**, no asset.
An impression with an asset is attributed to the asset's split in the contract's `assets`; an asset
the contract does not map is refused `asset_unmapped`. One with no asset is attributed to the
between split. The rule applies to the run's own signed schedule, so anyone holding the run finds
the same programme.
### 6.3 Unplaced impressions
An unplaced impression is attributed to the between split, in the class unplaced. It is revenue
the channel earned in some break that no record places in a programme, and the contract's between
split is how its parties agreed to share such revenue. It is never counted in the class presented:
a statement states it apart, and an owner sees how much of its revenue no record placed.
## 7. The statement
### 7.1 Layout
| field | size | meaning |
|---|---|---|
| `magic` | 4 | `WRS1` (ASCII) |
| `version` | 1 | 1 in this revision |
| `contract` | 32 | the contract's digest (§3.3) |
| `party` | 32 | the party it is for: the operator or an owner |
| `start`, `end` | 8 each | u64: the period it states |
| `currency` | 3 | the contract's |
| `invoices` | 2 + 32 × *i* | a u16 count, then each invoice's digest (receipt-ledger §6.7), in strictly ascending order |
| `runs` | 2 + 32 × *r* | a u16 count, then each run's identity (§4.2), in strictly ascending order |
| `lines` | 2 + 67 × *l* | a u16 count, then each line (§7.2), in strictly ascending order of (`split`, `class`) |
| `total` | 8 | u64: the sum of the lines' amounts |
| `sig` | 64 | §7.5 |
### 7.2 Lines
A line is 67 bytes:
| field | size | meaning |
|---|---|---|
| `split` | 2 | u16: the split's index |
| `class` | 1 | 1 presented, 2 beacons, 3 unplaced (§5.2) |
| `impressions` | 8 | u64: the impressions of the class attributed to the split |
| `revenue` | 8 | u64: the sum of their prices |
| `ppm` | 4 | u32: the party's share of the split |
| `amount` | 8 | u64: the party's part of the revenue (§7.3) |
| `cited` | 4 | u32: the records the line cites (§7.4) |
| `cites` | 32 | their citation digest (§7.4) |
There is one line for each split of which the party holds a share and each class with at least one
impression attributed to that split, and none other. A party's statement so states the whole
revenue of every split it shares in, and its part of each.
### 7.3 Revenue, shares and rounding
A line's revenue is divided among the parties of its split by the **largest-remainder rule**, so
that the parts are integers and sum to the revenue exactly:
1. For each party *i* of the split, in its order (§3.2), its **quota** is `revenue` × `ppm`*ᵢ*,
computed in integers wide enough not to overflow; its **base** is ⌊quota / 10^6⌋, and its
**remainder** is quota − base × 10^6.
2. The **leftover** is `revenue` less the sum of the bases. It is less than the count of parties.
3. The parties with the largest remainders, as many as the leftover, each receive one more than
their base; between equal remainders, the earlier party in the order receives it first.
4. A party's **part** is its base, plus one when step 3 gives it one.
The parts of a split's parties sum to its revenue, whoever's statement states them: the operator's
statement and each owner's state the same revenue for a split and class, and their parts sum to
it. Rounding is applied once per split and class over the statement's period, never per
impression, so it does not accumulate.
### 7.4 Citations
Each line cites the records it came from: for each impression attributed to its split in its class,
the impression's records (§5.2), the run that placed it (kind 10, its identity) when it has an
instant, and the invoice that billed it (kind 11, its digest). `cited` is their number, each record
counted once, and `cites` is SHA-256 over the bytes `wai:share-cites` (ASCII) and 0x01, followed by
each cited record's `kind` (u8) and identity (32), in ascending order of (`kind`, identity). A line
that cites no record has no source (`line_unsourced`, §8.2 step 7). An owner shows one line to an
auditor with the records it cites, the contract and the runs. Kinds 10 and 11, like every kind a
citation digest names, are registered in [receipt-ledger](receipt-ledger.md) §5.2.
### 7.5 Digest and signature
The **digest** of a statement is SHA-256 over its bytes before `sig`. Its signature is Ed25519 by
the contract's `operator` over the bytes `wai:share-statement` (ASCII) and 0x01, followed by the
bytes the digest covers, verified strictly (SPEC §7 *Signatures*).
### 7.6 Reading and rules
A reader reads exactly the bytes it is given as one statement, as §3.4 reads a contract with `WRS1`
for `WRC1` (`truncated`, `malformed`, `unknown_version`), and refuses as
`share_statement_invalid` one that breaks any of these:
- `start` is below `end`; `currency` is three capitals `A`–`Z`; `party` is not zero;
- `invoices`, `runs` and `lines` are each in the strictly ascending order §7.1 gives it;
- each line's `class` is 1, 2 or 3, its `impressions` and `cited` above 0, its `ppm` 1 to 10^6, and
its `amount` at most its `revenue`.
Its signature is checked against the contract's operator (§8.2 step 3).
## 8. Verification
### 8.1 Inputs
A verifier is given the **contract**; the **other contracts** it holds of the operator or of the
channel, for the overlap check; the **statement**; the **runs**; and the **ledger documents**. Its
verification document holds them as one JSON object with exactly the members `contract` and
`statement` (hexadecimal), `contracts` (a list of hexadecimal strings), `runs` (a list of runs in
their carriage, §4.1) and `ledgers` (a list of ledger documents, receipt-ledger §7.1). A document
that is not such an object is refused `malformed`.
### 8.2 Steps
It runs these steps and stops at the first refusal, which it reports with the reason and where:
1. **The contract** (§3.4), at `contract`; a signature's refusal at `contract` and `operator`, or
`contract`, `owner` and the owner's index: `truncated`, `malformed`, `unknown_version`,
`contract_invalid`, `split_not_whole`, `bad_key`, `bad_signature`, `not_countersigned`.
2. **The other contracts**, each at `contracts` and its index, a signature's place after it: each
is read and verified as step 1, with those reasons; one whose digest is the contract's is the
contract and is passed over; one that overlaps the contract (§3.5) is `contract_overlap`.
3. **The statement**, at `statement`: it reads (§7.6: `truncated`, `malformed`, `unknown_version`,
`share_statement_invalid`); its `contract` is the contract's digest, its `party` the operator
or an owner of the contract, and its `currency` the contract's: otherwise `contract_mismatch`;
its period lies within the contract's: otherwise `outside_contract`; its signature verifies
under the operator (§7.5): `bad_key`, `bad_signature`.
4. **The runs**, each at `run` and its index: it reads (§4.1: `malformed`); it verifies (§4.3:
`run_invalid`); its `channel_id` is the contract's `channel` and its `signer_pubkey` the
`channel_signer`: otherwise `run_foreign`; its window shares an instant with the window of a
run before it: `run_overlap`.
5. **The ledger documents**, each at `ledger` and its index: it is verified as receipt-ledger §7.2
verifies one, a refusal being that extension's reason with its place after the index; its
invoice's `seller` is the operator and its `currency` the contract's: otherwise
`invoice_foreign`; its period lies within the statement's: otherwise `outside_period`; and no
record of kinds 6 to 9 in its invoice's `records` is in the `records` of an invoice before it,
and no ad receipt of its sources names the room, the cue and the session an ad receipt of a
document before it names: otherwise `impression_duplicate`. A ledger refuses a record billed
twice within one invoice and one chain (receipt-ledger §5.2, §6.9); this step refuses one billed
by two invoices of a statement, under two tariffs among them.
6. **The impressions** (§5.2), in the order the documents give them, each at `ledger`, its index,
`source` and the source's index: an impression with an instant (§5.3) whose instant does not lie
within the statement's period is `outside_period`; one that aired in no window (§6.1) is
`outside_window`; one whose asset the contract does not map (§6.2) is `asset_unmapped`.
7. **The figures**, at `statement`, recomputed from the impressions as §7.2 to §7.4 compute them for
the statement's party, and compared in this order:
1. a line whose split and class give the party no impression, or that states more impressions
than they give: `line_unsourced`, a line no signed record covers;
2. a line whose `cited` or `cites` is not the records it came from: `citation_mismatch`;
3. any other difference (the invoices, the runs, a line missing, fewer impressions, a revenue, a
`ppm`, an amount, the `total`): `share_statement_mismatch`.
A figure that cannot be held in its field is a difference.
### 8.3 Outcome
- **refuse**: the reason and where.
- **accept**: the statement's digest; the contract's digest; the party and whether it is the
operator or an owner; the currency and the period; the invoices and the runs; each impression of
the contract's carriers, in the order of step 6, with the ledger document and source it came
from, its records, its class and price, its instant, run, entry and asset (each `null` when it has
none) and its split; each line with its split, class name, impressions, revenue, the party's
`ppm` and amount, its cited records, and the **division**, every party of its split with its
`ppm` and part; and the total.
The division is not signed: anyone holding the records computes it, and it shows that the parts of
a split sum to its revenue.
## 9. The statement and the receipt ledger
A statement composes with the ledger and duplicates none of it:
- **Its sources are the ledger's.** The impressions a statement shares are those of the `ads` and
`stitched` sources of invoices the ledger accepted, verified as the ledger verifies them: by
live-ads with ad-presentation, and by stitched-presentation. A statement recomputes no decision,
no claim and no presentation; it reads the ledger's counts and verdicts.
- **Its prices are the tariff's.** An impression's revenue is its invoice's price for the unit that
billed it, so an owner's revenue is a part of what the buyer was invoiced, never a figure the
operator states.
- **Its records are the invoice's.** Each impression is named by the records the invoice holds,
and the statement cites the invoice itself. A statement adds the two things the ledger does not
hold: the instant of each impression on the channel's program-as-run, and the split the contract
gives the programme that aired at that instant.
- **An operator that bills a channel's breaks** puts the sources of the channel's carriers into the
ledger as the ledger receives any source (receipt-ledger §5.1, §9), under a tariff whose scope
names the carriers' deciders and stitchers (receipt-ledger §4.4). The verification document the
ledger keeps with each invoice (receipt-ledger §9: `GET /v1/invoices/{digest}`) is the ledger
document a statement is given, unchanged.
Paying an owner its part, and an owner invoicing the operator for it, are not sources the ledger
defines, and this revision defines none.
## 10. Operations
`wai` runs the operations of this extension, under the agent rules of the operation catalogue (the
`--json` envelope, the exit codes, `--dry-run` on every operation that writes, `--yes` before a file
is replaced). A refusal by this text has its reason as the operation's code.
| operation | command line | does |
|---|---|---|
| `contract.create` | `wai contract create <terms.json> --key <file> [--out F]` | the operator's contract from its terms, signed by the operator with every countersignature zero (§3.3) |
| `contract.update` | `wai contract accept <contract> --key <file> [--out F]` | an owner's countersignature added, after the contract's rules and the operator's signature are checked |
| `statement.create` | `wai statement create <inputs.json> --party <key> --start µs --end µs --key <file> [--out F]` | the statement of a party and a period, computed from a verification document without its statement (§8.1), signed by the operator, written with its document |
| `statement.verify` | `wai statement verify <document.json>` | a verification document's outcome (§8.3); exit 1 with the reason on a refusal |
## 11. Reasons
The codes this extension adds, each with where it is raised:
| reason | raised at |
|---|---|
| `contract_invalid` | §3.4, §8.2 steps 1 and 2 |
| `split_not_whole` | §3.2, §3.4, §8.2 steps 1 and 2 |
| `not_countersigned` | §3.3, §8.2 steps 1 and 2 |
| `contract_overlap` | §3.5, §8.2 step 2 |
| `share_statement_invalid` | §7.6, §8.2 step 3 |
| `contract_mismatch` | §8.2 step 3 |
| `outside_contract` | §8.2 step 3 |
| `run_invalid` | §4.3, §8.2 step 4 |
| `run_foreign` | §8.2 step 4 |
| `run_overlap` | §6.1, §8.2 step 4 |
| `invoice_foreign` | §5.1, §8.2 step 5 |
| `impression_duplicate` | §8.2 step 5 |
| `outside_window` | §6.1, §8.2 step 6 |
| `asset_unmapped` | §6.2, §8.2 step 6 |
| `share_statement_mismatch` | §8.2 step 7 |
A verifier of this extension also raises, with the meaning this text gives them: `truncated`,
`malformed` and `unknown_version` for a contract or a statement that does not read, and `malformed`
for a document or a run that does not read; `bad_key` and `bad_signature` for their signatures;
`outside_period` (receipt-ledger §10) for an invoice or an impression outside the statement's
period; `line_unsourced` (fleet §10) for a line no impression gives; `citation_mismatch`
(receipt-ledger §10) for a line's citations; and, at a ledger document, every code the receipt
ledger raises (receipt-ledger §10 and the codes it lists).
## 12. Conformance
[`live-conformance/share/`](../live-conformance/share/) holds the corpus, in one file,
`vectors.json`, written by `wai_revenue_share_vectors gen` from fixed seeds and from the sources of
the ledger corpus (`live-conformance/ledger/`): a room's break-form receipts under a policy that
requires claims and under one that does not, and three streams stitched by others, one of whose
markers are DASH events. The ledger documents, the runs and the contracts are held once, by name,
and each vector is a verification document (§8.1) naming them, with the outcome §8.3 gives it:
- **Accepted**: a channel's month, its two windows airing two owners' programmes with mid-roll
breaks and an ad slot between programmes, its impressions from two invoices of one chain, one of
the room's breaks and one of the stitched streams; the statement of each owner and of the
operator under the rule preceding, so that the three statements' parts of each split sum to its
revenue; and an owner's statement under the rules following and between.
- **Refused**: each reason of §11 at least once, each vector a control of one defect; each code §11
reuses; and a ledger document refused by the receipt ledger, at its place.
`wai_revenue_share_vectors verify` and the test `tests/revenue_share.rs` check that the file
regenerates byte for byte, that `wai-rs` gives every vector its outcome, that every reason of §11
refuses a vector, and that §11 lists exactly the reference's codes; the test also holds the worked
example, three statements of one month computed and then recomputed from the public records alone,
and the module's unit tests the rounding rule's ties. `live-conformance/revenue_share_verify.py` is
the second implementation, written from this text: it shares no code with `wai-rs`, verifies
signatures with WAI's strict rule written from RFC 8032 (`claim-conformance/_ed25519.py`) and BLAKE3
with `claim-conformance/_blake3.py`, verifies each ledger document with that corpus's own second
implementation (`receipt_ledger_verify.py`), reads every contract, run, marker and statement itself,
recomputes every attribution, line, division and citation, gives every vector the same outcome,
requires every mutated copy of the corpus to fail, and drops each of its own checks in turn and
requires the corpus to catch the weakened verifier.
## 13. Security and privacy considerations
- **Who signs what.** Each owner signs the splits and the asset map it is bound by; the channel
signer signs what aired; the decider, the stitcher and each player sign the impressions; the
buyer signs the prices; the operator signs the statement. An operator cannot move an
impression to another programme, price it at another price or round a part against an owner: the
verifier recomputes every line from records the operator did not sign alone.
- **What the operator still controls.** The records show what was invoiced from the contract's
carriers. An operator that sells a break without invoicing it, or carries the channel through a
room or a stitcher the contract does not name, keeps that revenue out of every statement; the
contract's carriers are what the owners agreed to, and an owner that suspects more carriers asks
for them by name.
- **The between split.** Revenue in an entry that is not a programme, and every unplaced
presentation, goes to the between split. An owner that agrees a between split in which it holds
no share agrees to receive nothing from such breaks, and a statement states their revenue apart
so that the owner sees it.
- **Double counting.** One invoice and one chain bill a record once (the ledger's rules); two
invoices of one statement may not bill one impression (`impression_duplicate`); two contracts
may not share one channel or carrier at once (`contract_overlap`); and two runs may not place one
instant (`run_overlap`). A verifier holding fewer contracts than the operator signed cannot see
an overlap with the ones it lacks.
- **What a statement reveals.** A statement names the invoices and runs it rests on, and a line
states its split's whole revenue: the co-owners of a split, and the operator, see the same
figures. A verification document holds the ledgers' sources, whose receipts name sessions and
whose claims name session keys made for one session alone (ad-presentation §3,
stitched-presentation §5.2). It travels between the operator, the owner and an auditor they
choose.