receipt-ledger: tariff version 3 prices media jobs per operation and bills the work done #550
Loading…
Reference in a new issue
No description provided.
Delete branch "wai/ledger-work-priced"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What and why
WAI's receipt ledger billed media jobs in a way that fails the physics test twice
(waiTransport
docs/decisions.mdD3, D4):wai.video.int_motionan encode (a motion search, then acodestream written) and a decode (a reconstruction) spend different energy for the same output
units. A version-2 job price is
(capability, unit, divisor, price), so it overprices one orunderprices the other, and waiTransport could not bill decodes at its decode price at all.
int_motiondecode receipt (kind 15), and §6.9 refused any record already billed. The codecsare deterministic, so a re-run of the same request gives the same output receipt and was refused
record_rebilled, although the work was done again: a new hop, new energy, a new OER/2 receipt.The version decision: tariff version 3
A job price gains
op_lenandoperationaftercapability, which a version-2 reader cannotread under its strict reading, so §4's own versioning rule makes this version 3:
WLT1andWLI1with version byte 3, the invoice laid out exactly as version 2's. Version 2 is untouched.Its tariff's buyer signed version 2's rules, so it reads, verifies and bills exactly as before,
outputs included. Every existing vector keeps its outcome. The one exception is the
invoice_versioncontrol, which now uses version 4 because 3 is defined.The rules (receipt-ledger §4.6, §5.2, §5.3, §6.3, §6.5, §6.9, §7.2, §7.3, §12)
operation, in strictly ascending order of (capability, operation, unit). A version-3 tariffwith no price for a job's (capability, operation) bills it nothing, as an unpriced capability
bills nothing today.
record (16) and its hops' OER/2 receipts (14). The output receipt (15) is cited, and carried in
the citation digest, but is not once-only. So:
record_rebilledacross the chain,record_duplicatewithin an invoice);in one invoice (
record_overlap). Version 3 does not: they are two runs, each billed from itsown hop, and no hop twice. Without this, "the same request run again" would be refused or billed
depending only on where the seller cut the invoice periods. The rule never held across invoices
and was defeated by a new job id, so it guarded nothing the once-only hop receipt does not.
once per receipt, which is a separate rule.
one output receipt, that receipt is cited once and its subpixels are counted for each job. The
line's cited records name each job and that output once. How many of the jobs delivered it is
stated by the jobs' sources (each pairs its job with its outputs), not by the cited records
alone.
tariff_invalid, as §4.3 reads it. The reference saidmalformed, unlike the text and thePython. No vector held one.
Both implementations, the corpus and CI
wai-rsreceipt_ledger:VERSION_3,prices_jobs,JobPrice::operation,JobFacts::operation,billed_once(version, kind), andcheck_across(version, …). The breakingAPI changes are named in the CHANGELOG.
receipt_ledger_verify.py: reads version 3 and applies its rules. It adds five mutated copies(the decode priced as the encode, the decode line named an encode, the shared output's subpixels
billed once, the re-run without its previous invoice, the re-run swapped for a hop replay). It
also drops two more of its own checks (
operation,output-once), and the corpus must catcheach.
wai/wai-ledger: applies the chain rule of the tariff's version. A new test issues theversion-3 chain byte for byte, bills the re-run and refuses the replayed hop.
under its seeds through a
pub(crate)helper.routes/vectors.jsonis unchanged.wai-conformance.ymlruns the new vectors and test, and itscomment says so.
wai-ledger.yml's comment names the new service test.wfsteps.py: 0 problems.Vectors (
live-conformance/ledger/vectors.json)The corpus goes from 60 vectors to 69. Of the 60, 59 keep their bytes and outcomes. The one
exception is
invoice_version, which moves from version 3 to 4. The 30 sources are unchanged,and 4 are added.
jobs_priced_per_operationint_motionencode at 500,000 per job and 20,000 per 10⁶ subpixels, decode at 250,000 and 10,000, and the av1 failover at its encode price.a_rerun_billedone_output_under_two_hops[14,14,15,16,16]) and its subpixels are billed twice.an_operation_the_tariff_does_not_pricea_hop_receipt_replayedrecord_rebilledat["source",0]: a new route record replaying job-0001's billed hop receipt.one_hop_under_two_linesrecord_duplicateat["source",1]: a job whose av1 fallback hop claims theint_motionencode job's hop receipt.job_prices_unordered_by_operationtariff_invalidat["tariff"]version_2_invoice_under_a_version_3_tarifftariff_mismatchat["invoice"]version_2_bills_an_output_oncerecord_rebilledat["source",0]: the same re-run under a version-2 chain.All the version-2 vectors are untouched, among them
output_claimed_by_two_jobs(record_duplicate)and
job_routed_twice(record_overlap).Tests (all green)
Framework host (
192.168.4.47,rb,CARGO_BUILD_JOBS=8, featuresopportunity_report):--lib receipt_ledger:: route_record:: revenue_share::: 21 passed. The new unit tests cover theversion-3 tariff reading back with every altered byte refused, ordering by operation, the
operation's form at 128 and 129 bytes, non-UTF-8,
billed_once, and a version-3 invoice.--test receipt_ledger: 5 passed, including the newversion_3_prices_each_operation_and_bills_the_work(488 s in debug).--test route_record: 6.--test revenue_share: 7.--test opportunity_report: 6. Theircorpora regenerate byte for byte.
wai_receipt_ledger_vectors verify: the corpus regenerates byte for byte, and every vector getsits stated outcome.
cargo check --features route_recordalone: the ledger helpers are gated off cleanly.wai/wai-ledger:cargo test --locked, 4 passed (the newa_rerun_is_billed_and_a_replayed_hop_is_notamong them), andcargo clippy --locked --all-targets -- -D warningsis clean.wai-rswith those features: no finding in any file this change touches.Studio (
100.100.141.123, Python 3.15.2 withcryptography):receipt_ledger_verify.py: 69/69 vectors, every reason of §10, 82 mutated copies fail, 25dropped checks caught.
revenue_share_verify.py,opportunity_report_verify.pyandroute_record_verify.pyall pass.The Framework's Python has no
cryptography, so its OER/2 verifier refuses every hop receiptthere, including the unchanged route corpus's. The Studio run is the Python result.
Repo text checks (
check-spec-refs,check-wai-section-refs, vanity metrics, comparative prose,tests-in-CI) pass.
wfsteps.py HEAD origin/mainreports 0 problems.Falsifications (each rule broken in the source, the corpus fails, then restored)
Rust ran on the Framework (
wai_receipt_ledger_vectors verify, profilefalsify). Python ran onthe Studio (
receipt_ledger_verify.check). Each mutation was checked to have applied exactly once,was restored with
git checkout, and the restored tree passes again.jobs_priced_per_operation,a_rerun_billed,one_output_under_two_hops,an_operation_the_tariff_does_not_price(citation and invoice mismatches)a_rerun_billed(→record_rebilled),one_output_under_two_hops(→record_duplicate)version_2_bills_an_output_once(→ accept),output_claimed_by_two_jobsa_hop_receipt_replayed(→ accept),one_hop_under_two_lines(→ accept)one_output_under_two_hops(→record_overlap)tariff_invalid)receipt_ledger_verify.py(operation, output once, hop once, overlap)receipt_ledger_verify.py's own sweep also drops each of its 25 checks (operationandoutput-onceamong them), and the corpus catches every one.wai breakingRun on the Framework against
4902e6679, the current main and this branch's base. It reports 4breaking changes, all acknowledged in the CHANGELOG, and exits 0:
record:corpus:wai/live-conformance/ledger/vectors.json,api:rust:wai::receipt_ledger::JobPrice::operation,api:rust:wai::receipt_ledger::JobFacts::operationandapi:rust:wai::receipt_ledger::check_across. It listsVERSION_3,billed_onceandprices_jobsas compatible additions.Found and not changed (also fail the physics test)
These are raised, not changed:
package.buildjob stays a package source (§4.5), billed once perpackage receipt. That receipt states no time, so a deterministic re-package with a model-based
(or unmetered) figure gives the same
receipt_hashand is refused, although the work was doneagain. A measured re-package gets a new hash only because its joules differ, so whether a
re-package bills depends on meter noise. That is the same output-keyed failure. The fix in kind
would bill a package job as a job source (route record and hop receipt once, the package receipt
as evidence). The package receipt also carries
work_units, which no tariff prices.packageprices stored bytes per 10⁹, once perreceipt. Storage cost accrues per byte-time, so one charge per receipt does not track it.
max_priceandspend_ceilinglimits, and its price ranking cost an encode and a decode alike,so a router can choose by a price the ledger will not bill.
(or one past the deadline) spent real energy that the invoice's energy statement leaves out.
Billing it nothing is right; stating no energy for it undercounts what the jobs cost.
waiTransport D7's argument (delivery cost scales with bytes) applies.
subject.outputto its output (thecodestream hash the decode receipt states) would let the cited records alone pair each job with
its output. That needs a route-record or OER binding rule.
🤖 Generated with Claude Code