-
Notifications
You must be signed in to change notification settings - Fork 0
PoC: register dedicated synchronizers via governance (rung 1/2) #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -100,6 +100,51 @@ instance HasCheckedFetch MemberTraffic ForDso where | |
| instance HasCheckedFetch MemberTraffic ForMemberTraffic where | ||
| contractGroupId MemberTraffic{..} = ForMemberTraffic with .. | ||
|
|
||
| -- Dedicated-synchronizer registration (PoC) | ||
| ------------------------------------------------ | ||
| -- | ||
| -- A "dedicated synchronizer" is a non-global synchronizer (stood up with the Splice software) | ||
| -- whose traffic we want to fund by burning CC on the global synchronizer. Before any CC can be | ||
| -- burned for such a synchronizer it is registered once, by DSO governance, via | ||
| -- `DsoRules_RegisterSynchronizer` (splice-dso-governance), which creates this contract. | ||
| -- | ||
| -- The registration binds a stable synchronizer id to the `operator` party that runs it. Two | ||
| -- consumers read it: | ||
| -- * `AmuletRules_BuyDedicatedSyncTraffic` (splice-amulet) authorizes a CC burn for a | ||
| -- synchronizer id only if a matching `RegisteredSynchronizer` is supplied (via explicit | ||
| -- disclosure), and uses `operator` as the observer of the resulting traffic record. | ||
| -- * the `operator` itself, which observes this contract and thus learns it is registered. | ||
| -- | ||
| -- This template lives in splice-amulet (not splice-dso-governance) so that `AmuletRules` can | ||
| -- `fetch` it: splice-dso-governance depends on splice-amulet, not the reverse. | ||
| template RegisteredSynchronizer with | ||
| dso : Party | ||
| -- ^ The DSO of the global synchronizer; sole signatory. A DSO vote creates this contract. | ||
| synchronizerId : Text | ||
| -- ^ Id of the dedicated synchronizer this registration authorizes CC-funded traffic for. | ||
| operator : Party | ||
| -- ^ The party operating the dedicated synchronizer. Observes this registration and every | ||
| -- traffic purchase for its synchronizer (see `DedicatedSyncTraffic`). | ||
| where | ||
| signatory dso | ||
| observer operator | ||
|
|
||
| ensure not (T.isEmpty synchronizerId) | ||
|
|
||
| -- | Public fetch choice, mirroring `OpenMiningRound_Fetch`: lets a buyer who is neither | ||
| -- signatory nor observer read this contract inside `AmuletRules_BuyDedicatedSyncTraffic` | ||
| -- when it is passed via explicit disclosure. Returns the contract unchanged. | ||
| nonconsuming choice RegisteredSynchronizer_Fetch : RegisteredSynchronizer | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Public fetch choice, copied from |
||
| with | ||
| p : Party -- ^ the reader (the buyer/provider); authorizes the fetch | ||
| controller p | ||
| do pure this | ||
|
|
||
| -- Group-id check for `fetchPublicReferenceData`: a `RegisteredSynchronizer` belongs to its DSO, | ||
| -- so reads are validated against the expected DSO party (mirrors the `MemberTraffic`/`ForDso` instance). | ||
| instance HasCheckedFetch RegisteredSynchronizer ForDso where | ||
| contractGroupId RegisteredSynchronizer{..} = ForDso with .. | ||
|
|
||
| instance Patchable BaseRateTrafficLimits where | ||
| patch new base current = BaseRateTrafficLimits with | ||
| burstAmount = patch new.burstAmount base.burstAmount current.burstAmount | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| -- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. | ||
| -- SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| -- | PoC (dedicated-synchronizer traffic): register a dedicated synchronizer through a DSO | ||
| -- governance vote and assert the resulting `RegisteredSynchronizer` contract. | ||
| -- | ||
| -- This is the first rung of the ladder: it only proves that governance can create the | ||
| -- registry entry that a later CC-funded traffic purchase will read. Buying traffic against a | ||
| -- registered synchronizer is exercised in the splice-amulet test (next PR). | ||
| module Splice.Scripts.TestRegisterSynchronizer where | ||
|
|
||
| import DA.Assert | ||
|
|
||
| import Daml.Script | ||
|
|
||
| -- The governance action + its argument record. | ||
| import Splice.DsoRules | ||
| -- The registry contract created by the vote. It lives in splice-amulet (AmuletRules must be | ||
| -- able to fetch it), and splice-dso-governance-test data-depends on splice-amulet. | ||
| import Splice.DecentralizedSynchronizer (RegisteredSynchronizer) | ||
|
|
||
| -- Shared helpers that spin up a 4-SV network and drive a vote end-to-end. | ||
| import Splice.Scripts.DsoTestUtils | ||
|
|
||
| -- | Register a dedicated synchronizer via a supermajority SV vote, then verify the registration: | ||
| -- signed by the DSO, naming the operator, and observed by that operator. | ||
| test_RegisterSynchronizer_viaVote : Script () | ||
| test_RegisterSynchronizer_viaVote = do | ||
| -- 4-SV network; `dso` is the DSO party, `sv1..sv4` are the Super Validators. | ||
| (app, dso, (sv1, sv2, sv3, sv4)) <- initMainNet | ||
|
|
||
| -- The party that will operate the dedicated synchronizer. | ||
| operator <- allocateParty "dedicatedSyncOperator" | ||
| let dedicatedSyncId = "dedicated-sync::1220dededededededededededededededededededededededededededededede" | ||
|
|
||
| -- Supermajority SV vote to register the dedicated synchronizer. `initiateAndAcceptVote` routes | ||
| -- through DsoRules_RequestVote -> DsoRules_CastVote (x3) -> DsoRules_CloseVoteRequest, which on | ||
| -- acceptance calls executeActionRequiringConfirmation, dispatching to DsoRules_RegisterSynchronizer. | ||
| initiateAndAcceptVote app [sv1, sv2, sv3, sv4] $ | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Drives the full governance path ( |
||
| ARC_DsoRules with | ||
| dsoAction = SRARC_RegisterSynchronizer DsoRules_RegisterSynchronizer with | ||
| synchronizerId = dedicatedSyncId | ||
| operator | ||
|
|
||
| -- The registration now exists on-ledger with exactly the fields we voted for. | ||
| registrations <- query @RegisteredSynchronizer dso | ||
| case registrations of | ||
| [(_cid, reg)] -> do | ||
| reg.dso === dso | ||
| reg.synchronizerId === dedicatedSyncId | ||
| reg.operator === operator | ||
| _ -> abort $ "expected exactly one RegisteredSynchronizer, got " <> show (length registrations) | ||
|
|
||
| -- The operator is an observer, so it can see its own registration. | ||
| operatorView <- query @RegisteredSynchronizer operator | ||
| length operatorView === 1 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,7 +27,9 @@ import Splice.Round | |
| import Splice.Types | ||
| import Splice.ValidatorLicense | ||
| import Splice.Wallet.Subscriptions | ||
| import Splice.DecentralizedSynchronizer (MemberTraffic) | ||
| -- `RegisteredSynchronizer` (PoC): created by `DsoRules_RegisterSynchronizer` below; the template | ||
| -- itself lives in splice-amulet because AmuletRules must be able to fetch it. | ||
| import Splice.DecentralizedSynchronizer (MemberTraffic, RegisteredSynchronizer(..)) | ||
|
|
||
| import qualified Splice.CometBft as CometBft | ||
| import Splice.Ans | ||
|
|
@@ -117,6 +119,10 @@ data DsoRules_ActionRequiringConfirmation | |
| -- ^ Create TransferCommandCounter contract for the given sender if it does not already exist | ||
| | SRARC_CreateUnallocatedUnclaimedActivityRecord DsoRules_CreateUnallocatedUnclaimedActivityRecord | ||
| -- ^ Voted action to create an UnallocatedUnclaimedActivityRecord contract. | ||
| | SRARC_RegisterSynchronizer DsoRules_RegisterSynchronizer | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New variant on the closed |
||
| -- ^ PoC (dedicated-synchronizer traffic): voted action to register a dedicated (non-global) | ||
| -- synchronizer by binding its id to an operator party, so its traffic can be funded in CC. | ||
| -- Executed via a DSO vote; see the `DsoRules_RegisterSynchronizer` choice. | ||
| | SRARC_CreateBootstrapExternalPartyConfigStateInstruction DsoRules_CreateBootstrapExternalPartyConfigStateInstruction | ||
| -- ^ Create BootstrapExternalPartyConfigStateInstruction | ||
| | SRARC_UpdateFeaturedAppRight DsoRules_UpdateFeaturedAppRight | ||
|
|
@@ -332,6 +338,10 @@ data DsoRules_AmuletRules_ConvertFeaturedAppActivityMarkersResult = DsoRules_Amu | |
| data DsoRules_CreateUnallocatedUnclaimedActivityRecordResult = DsoRules_CreateUnallocatedUnclaimedActivityRecordResult with | ||
| unallocatedUnclaimedActivityRecordCid : ContractId UnallocatedUnclaimedActivityRecord | ||
|
|
||
| -- | Result of `DsoRules_RegisterSynchronizer`: the cid of the created registration. | ||
| data DsoRules_RegisterSynchronizerResult = DsoRules_RegisterSynchronizerResult with | ||
| registeredSynchronizerCid : ContractId RegisteredSynchronizer | ||
|
|
||
| data DsoRules_AllocateUnallocatedUnclaimedActivityRecordResult = DsoRules_AllocateUnallocatedUnclaimedActivityRecordResult with | ||
| unclaimedActivityRecordCid : ContractId UnclaimedActivityRecord | ||
| optUnclaimedRewardCid : Optional (ContractId UnclaimedReward) | ||
|
|
@@ -1680,6 +1690,22 @@ template DsoRules with | |
| expiresAt | ||
| pure $ DsoRules_CreateUnallocatedUnclaimedActivityRecordResult with .. | ||
|
|
||
| -- | PoC (dedicated-synchronizer traffic): governed registration of a dedicated synchronizer. | ||
| -- Creates a `RegisteredSynchronizer` (in splice-amulet) binding `synchronizerId` to its | ||
| -- `operator`. This is the only way the registry is populated; `AmuletRules_BuyDedicatedSyncTraffic` | ||
| -- then reads that contract (via explicit disclosure) to authorize CC burns for the synchronizer. | ||
| -- `controller dso` + `nonconsuming`: a vote reaches this via `executeActionRequiringConfirmation`, | ||
| -- and the `DsoRules` contract is not archived by exercising it. | ||
| nonconsuming choice DsoRules_RegisterSynchronizer : DsoRules_RegisterSynchronizerResult | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Governed create, mirroring |
||
| with | ||
| synchronizerId : Text -- ^ stable id of the dedicated synchronizer to register | ||
| operator : Party -- ^ the party operating that synchronizer | ||
| controller dso | ||
| do | ||
| require "synchronizerId is non-empty" (not (T.isEmpty synchronizerId)) | ||
| registeredSynchronizerCid <- create RegisteredSynchronizer with dso; synchronizerId; operator | ||
| pure $ DsoRules_RegisterSynchronizerResult with .. | ||
|
|
||
| nonconsuming choice DsoRules_AllocateUnallocatedUnclaimedActivityRecord : DsoRules_AllocateUnallocatedUnclaimedActivityRecordResult | ||
| with | ||
| unallocatedUnclaimedActivityRecordCid : ContractId UnallocatedUnclaimedActivityRecord | ||
|
|
@@ -1816,6 +1842,8 @@ executeActionRequiringConfirmation dso dsoRulesCid amuletRulesCid act = case act | |
| SRARC_CreateExternalPartyAmuletRules choiceArg -> void $ exercise dsoRulesCid choiceArg | ||
| SRARC_CreateTransferCommandCounter choiceArg -> void $ exercise dsoRulesCid choiceArg | ||
| SRARC_CreateUnallocatedUnclaimedActivityRecord choiceArg -> void $ exercise dsoRulesCid choiceArg | ||
| -- PoC: an accepted vote to register a dedicated synchronizer runs the choice on DsoRules. | ||
| SRARC_RegisterSynchronizer choiceArg -> void $ exercise dsoRulesCid choiceArg | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dispatch arm: an accepted |
||
| SRARC_CreateBootstrapExternalPartyConfigStateInstruction choiceArg -> void $ exercise dsoRulesCid choiceArg | ||
| ARC_AnsEntryContext with .. -> do | ||
| void $ fetchChecked (ForDso with dso) ansEntryContextCid | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Crux of rung 1: this DSO-signed contract binds a dedicated synchronizer id to its
operatorparty. It is created only by a governance vote (DsoRules_RegisterSynchronizer) and observed by the operator. It lives in splice-amulet rather than splice-dso-governance because the rung-2AmuletRules_BuyDedicatedSyncTrafficchoice mustfetchit, and splice-dso-governance depends on splice-amulet (one-way), not the reverse.