PoC: extension reward reporting + expansion (Daml) - #8
Open
timwu20 wants to merge 4 commits into
Open
Conversation
…) [ci] The registered synchronizer's operator commits its per-round activity on the decentralized synchronizer and expands the commitment into reward coupons through the CIP-104 machinery: - DedicatedSynchronizerState: per-synchronizer reporting state; the operator's ReportActivity choice records a Merkle root hash + round total, enforces strictly increasing rounds, accumulates the total ever reported, and creates a per-round ExtensionActivityReport - AmuletRules_StartProcessingExtensionRewardsV2 (vote-dispatched via appended CRARC constructor): consumes a report and creates the root ProcessRewardsV2 with the report's root hash, the issuance rate, and the operator as expander - ProcessRewardsV2: appended expander + issuancePerActivityUnit; the expander is observer and ProcessBatch controller when set; children inherit both; leaves mint activity weight * rate. None/None is unchanged gsync behavior - Daml Script tests: report/expand happy path with coupon amounts, report monotonicity + authorization negatives, hash-binding negatives, and the 4-SV vote path end to end Signed-off-by: Timothy Wu <tim.wu@chainsafe.io>
…artifacts [ci] Signed-off-by: Timothy Wu <tim.wu@chainsafe.io>
5 tasks
CI's output scanner fails jobs on any DsWarning even when sbt succeeds: - replace the 8-tuple setup return with a ReportedActivitySetup record (Daml has no instances for tuples of size > 5) Review-pass fixes bundled in: - StartProcessingExtensionRewardsV2 doc comment moved to the -- ^ form after the choice declaration - roundClosedAt field doc covers the extension case (start-of-processing time; reports carry no round-close time) - new dry-run expansion test (contracts consumed, no coupons minted) Signed-off-by: Timothy Wu <tim.wu@chainsafe.io>
…als [ci] Close the gap between an extension's reported activity total and what its committed tree can mint: - StartProcessingExtensionRewardsV2 takes the vote-attested purchasedTotal and requires reported activity <= purchased traffic - BatchOfWeightedBatches (appended Batch constructor): internal nodes of an extension tree declare each child subtree's weight inside the hashed commitment; ProcessRewardsV2 carries a weightBudget threaded from the reported total, and expansion requires each level's declared weights to fit its budget, bounding total minting by the reported total - path exclusivity: unweighted internal nodes only without a rate (the decentralized synchronizer's own flow, which is unchanged - its trees are recomputed and vote-confirmed by every SV, so no budget is needed); weighted nodes only with one - tests: over-declared internal node, over-weight leaf, report exceeding purchased, plain-batch smuggling Signed-off-by: Timothy Wu <tim.wu@chainsafe.io>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Daml PoC of the extension reward integration sketched in the design doc's Section H, stacked on the buy rung (#2). On-ledger pieces only — the SV-side rate computation (tranche split by burn share, the voted per-synchronizer weight, the report timeout) and the operator-side automation stay off-ledger and are not part of this PR.
What this does
Per round, a registered synchronizer's operator commits its activity on the decentralized synchronizer, and later expands that commitment into reward coupons through the existing CIP-104 machinery:
DedicatedSynchronizerState(new, splice-amulet): per-synchronizer reporting state. The operator'sReportActivitychoice records a Merkle root hash over its activity records plus the round total, enforces strictly increasing rounds, accumulates a running total-ever-reported, and creates a per-roundExtensionActivityReport.AmuletRules_StartProcessingExtensionRewardsV2(new choice, vote-dispatched via appendedCRARC_StartProcessingExtensionRewardsV2): consumes a report and creates the rootProcessRewardsV2carrying the report's root hash, the issuance rate computed by DSO automation, the operator as expander, and the report's total as the root weight budget. The vote also attests the synchronizer's purchased traffic total, and the choice rejects a report exceeding it.ProcessRewardsV2(extended, per Section H "noting that party on the ProcessRewardsV2 contracts" + "push issuance per activity record into process rewards"): appendedexpander : Optional Party,issuancePerActivityUnit : Optional Decimal, andweightBudget : Optional Decimal; the expander is an observer (it must see the contracts to act) and the controller ofProcessBatchwhen set; leaf expansion mints coupons at activity weight × rate.Nones everywhere (all existing contracts and the gsync path) is byte-for-byte today's behavior — including that the delegate-basedDsoRules_ProcessRewardsV2_ProcessBatchwrapper cannot touch extension contracts.BatchOfWeightedBatches(appendedBatchconstructor): internal nodes of an extension tree declare each child's subtree weight inside the hashed commitment. Expansion checks the declared weights against the node's budget and endows each child with its declared weight; leaves check their allowances against theirs. Weighted nodes require a rate and unweighted ones reject it, so the two tree shapes cannot mix within one round's expansion.Economic bound
The decentralized synchronizer's own flow needs none of this because every SV recomputes the full tree before confirming the root — expansion is transcription. An extension tree has a single author the SVs cannot recompute; what stands in is a chain of on-ledger checks, each anchored one level up:
A binds the report to reality: the confirming vote carries the purchased total (from
MemberTraffic, which SVs already see), andAmuletRules_StartProcessingExtensionRewardsV2requirestotalActivity ≤ purchasedTotal. B binds the tree to the report: the root's budget is the reported total, every internal node's children must fit its budget, and every leaf's allowances must fit its own — so no expansion order or tree shape can mint past what was reported. An operator can still distribute weight among its own parties as it pleases; what it cannot do is mint more than the round's attested activity.How it's verified (Daml Script)
TestExtensionRewardReporting(splice-amulet-test): full report → start → expand flow with a two-level weighted tree, coupon amounts = weight × rate; report monotonicity/negative-total/wrong-party negatives; expansion security (tampered pre-image fails the hash check, the DSO party cannot expand extension batches, a leaf cannot be replayed against the wrong hash); economic bound (a report exceeding the attested purchased total is rejected; an internal node over-declaring its children's weights and a leaf minting past its declared weight both fail; a plain unweighted batch cannot smuggle a rate).TestExtensionRewardVote(splice-dso-governance-test): the governance path — register via vote, report as operator, start processing via a 4-SV supermajority vote, expand, assert the coupon.Full amulet, wallet, and dso-governance suites pass.
Assumptions (Section H implies but does not pin — flagging for review)
Decimal, reusingMintingAllowanceas the leaf type, with the weight × rate multiplication inNumeric 10at expansion. The committed tree is in activity units because the rate does not exist at report time (it depends on all synchronizers' reports and burns).DedicatedSynchronizerStaterather than onRegisteredSynchronizer(Section H says "possibly in registry contract"); kept separate to leave the registration stable. Production wiring of the state's creation (at registration) follows the report-home decision.CalculateRewardsV2is untouched: the extension flow starts from the report contract; the timeout / count-as-zero behavior is SV-side.providersWithWrongVettingStateremains caller-supplied at expansion; extension expansions pass what the operator knows, with the unhide trigger as the correction path.Tracked in
Implements E10-1 (ChainSafe/canton-extending-mainnet#47); part of Epic E10 (#22). The SV-side integration and operator-side automation are tracked as follow-ups (#57 / #58).