Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion daml/dars.lock
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,15 @@ splice-dso-governance 0.1.24 4974c654485d4ecaa6b5caf8ef3c2679efa8195c4b50d4965a8
splice-dso-governance 0.1.25 b41ffa8aadafc8ae78eeb39b201eca9bc96470267d36553badd5df2a0587b16f
splice-dso-governance 0.1.26 d65173dabe1b5e7278ffb66c55defb123f72df18218c7e317bcc4f703f08a84e
splice-dso-governance 0.1.27 ab73d34dddab433044f560603f2822e5b26d9af98bd6ea8a0e9a25c5f717ab9a
splice-dso-governance 0.1.28 f70766cc94255f385f0c6e12c7635ef1adfeecde30de873db646db5a2cf8e94b
splice-dso-governance 0.1.3 b0ae3cc03e418790305a3c15f761fe495572de5827f8d322fb8b96996b783c13
splice-dso-governance 0.1.4 dc24fd18b4d151cd1e0ff6bfb7438bafb2f50fe076d0f16f50565e60b153a0be
splice-dso-governance 0.1.5 9e3ca1d22ad495dfabf3d61acae3dc1a7718f527f02092280b58cf69edfdc84c
splice-dso-governance 0.1.6 4e7653cfbf7ca249de4507aca9cd3b91060e5489042a522c589d3c4199580cd8
splice-dso-governance 0.1.7 d406eba1132d464605f4dae3edf8cf5ecbbb34bd8edef0e047e7e526d328718c
splice-dso-governance 0.1.8 1790a114f83d5f290261fae1e7e46fba75a861a3dd603c6b4ef6b67b49053948
splice-dso-governance 0.1.9 9ee83bfd872f91e659b8a8439c5b4eaf240bcf6f19698f884d7d7993ab48c401
splice-dso-governance-test 0.1.33 bb97b29d63309dad1c7d9af45a5f425e4fae18ddc83f5c20755e21f4d21bcd26
splice-dso-governance-test 0.1.34 0ba60677a1ce30ffb2ff86e89485bad6925c0541d5e6977f791bff403464b2da
splice-token-standard-test 1.0.13 a556574314ab5ecbfa04b04a6b6c9259cf90388461fe307ba712257ad5993a6b
splice-token-test-dummy-holding 0.0.1 1cd171c6c42ab46dc9cf12d80c6111369e00cea5cdf054924b4f26ce94b1ef5b
splice-token-test-dummy-holding 0.0.2 4f40fb033ef3db89623642c1b494e846097fa32af138b3864a63aa15937a323d
Expand Down
Binary file added daml/dars/splice-dso-governance-0.1.28.dar
Binary file not shown.
2 changes: 1 addition & 1 deletion daml/splice-dso-governance-test/daml.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
sdk-version: 3.4.11
name: splice-dso-governance-test
source: daml
version: 0.1.33
version: 0.1.34
dependencies:
- daml-prim
- daml-stdlib
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,128 @@ testSvGovernanceVoterBindingLifecycle = do
None <- queryContractId dso rotateResult.bindingCid
pure ()

-- | Tests governance-voter casting while preserving represented-SV vote slots.
testGovernanceVoterCastPath : Script ()
testGovernanceVoterCastPath = do
(_app, dso, (sv1, sv2, _sv3, _sv4)) <- initMainNet
governanceVoter1 <- allocateParty "cast-governance-voter-1"
governanceVoter2 <- allocateParty "cast-governance-voter-2"
wrongVoter <- allocateParty "wrong-cast-governance-voter"
newSv <- allocateParty "cast-new-sv"

[(dsoRulesCid, _)] <- query @DsoRules dso
[(_, openRound), _, _] <- sortOn (._2.round) <$> query @OpenMiningRound dso
bindingCid <- submit sv1 $ createCmd SvGovernanceVoter with
dso
sv = sv1
governanceVoter = governanceVoter1

let governanceVote signer accept body = Vote with
sv = sv1
castBy = signer
castByRole = VCR_GovernanceVoter
accept
reason = Reason with url = ""; body
optCastAt = None
let operatorVote accept body = Vote with
sv = sv1
castBy = sv1
castByRole = VCR_Operator
accept
reason = Reason with url = ""; body
optCastAt = None
let requestOffboardSv = submit (actAs sv1 <> readAs dso) $ exerciseCmd dsoRulesCid DsoRules_RequestVote with
requester = sv1
action = ARC_DsoRules with
dsoAction = SRARC_OffboardSv DsoRules_OffboardSv with sv = sv2
reason = Reason with url = ""; body = "cast path"
targetEffectiveAt = None
voteRequestTimeout = Some (days 7)

requestResult <- requestOffboardSv
passTime (minutes 1)
castResult <- submit (actAs governanceVoter1 <> readAs dso) $ exerciseCmd dsoRulesCid DsoRules_CastGovernanceVote with
requestCid = requestResult.voteRequest
bindingCid
vote = governanceVote governanceVoter1 False "governance voter update"
Some request <- queryContractId dso castResult.voteRequest
Map.size request.votes === 1
case Map.lookup request.requester request.votes of
None -> fail "represented SV vote missing"
Some vote -> do
vote.sv === sv1
vote.castBy === governanceVoter1
vote.castByRole === VCR_GovernanceVoter
vote.accept === False

passTime (minutes 1)
operatorResult <- submit (actAs sv1 <> readAs dso) $ exerciseCmd dsoRulesCid DsoRules_CastVote with
requestCid = castResult.voteRequest
vote = operatorVote True "operator overwrite"
Some request <- queryContractId dso operatorResult.voteRequest
case Map.lookup request.requester request.votes of
None -> fail "operator vote missing"
Some vote -> do
vote.castBy === sv1
vote.castByRole === VCR_Operator
vote.accept === True

passTime (minutes 1)
castResult <- submit (actAs governanceVoter1 <> readAs dso) $ exerciseCmd dsoRulesCid DsoRules_CastGovernanceVote with
requestCid = operatorResult.voteRequest
bindingCid
vote = governanceVote governanceVoter1 False "governance voter overwrite"
Some request <- queryContractId dso castResult.voteRequest
case Map.lookup request.requester request.votes of
None -> fail "governance-voter vote missing"
Some vote -> do
vote.castBy === governanceVoter1
vote.castByRole === VCR_GovernanceVoter
vote.accept === False

unsupportedRequest <- submit (actAs sv1 <> readAs dso) $ exerciseCmd dsoRulesCid DsoRules_RequestVote with
requester = sv1
action = ARC_DsoRules with
dsoAction = SRARC_AddSv DsoRules_AddSv with
newSvParty = newSv
newSvName = "cast-new-sv"
newSvRewardWeight = 1
newSvParticipantId = "cast-new-sv-participant"
joinedAsOfRound = openRound.round
reason = Reason with url = ""; body = "unsupported action"
targetEffectiveAt = None
voteRequestTimeout = Some (days 7)
submitMustFail (actAs governanceVoter1 <> readAs dso) $ exerciseCmd dsoRulesCid DsoRules_CastGovernanceVote with
requestCid = unsupportedRequest.voteRequest
bindingCid
vote = governanceVote governanceVoter1 False "unsupported"

wrongVoterRequest <- requestOffboardSv
submitMustFail (actAs wrongVoter <> readAs dso) $ exerciseCmd dsoRulesCid DsoRules_CastGovernanceVote with
requestCid = wrongVoterRequest.voteRequest
bindingCid
vote = governanceVote wrongVoter False "wrong voter"

rotateResult <- submit sv1 $ exerciseCmd bindingCid RotateGovernanceVoter with
newGovernanceVoter = governanceVoter2
rotatedRequest <- requestOffboardSv
submitMustFail (actAs governanceVoter1 <> readAs dso) $ exerciseCmd dsoRulesCid DsoRules_CastGovernanceVote with
requestCid = rotatedRequest.voteRequest
bindingCid = rotateResult.bindingCid
vote = governanceVote governanceVoter1 False "rotated old voter"
passTime (minutes 1)
_ <- submit (actAs governanceVoter2 <> readAs dso) $ exerciseCmd dsoRulesCid DsoRules_CastGovernanceVote with
requestCid = rotatedRequest.voteRequest
bindingCid = rotateResult.bindingCid
vote = governanceVote governanceVoter2 False "rotated new voter"

_ <- submit sv1 $ exerciseCmd rotateResult.bindingCid ClearGovernanceVoter
clearedRequest <- requestOffboardSv
submitMustFail (actAs governanceVoter2 <> readAs dso) $ exerciseCmd dsoRulesCid DsoRules_CastGovernanceVote with
requestCid = clearedRequest.voteRequest
bindingCid = rotateResult.bindingCid
vote = governanceVote governanceVoter2 False "cleared binding"

-- | Tests the Phase 1 governance-voter action taxonomy.
testGovernanceVoterActionTaxonomy : Script ()
testGovernanceVoterActionTaxonomy = do
Expand Down
2 changes: 1 addition & 1 deletion daml/splice-dso-governance/daml.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
sdk-version: 3.4.11
name: splice-dso-governance
source: daml
version: 0.1.27
version: 0.1.28
dependencies:
- daml-prim
- daml-stdlib
Expand Down
42 changes: 42 additions & 0 deletions daml/splice-dso-governance/daml/Splice/DsoRules.daml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import Splice.Ans
import Splice.SvOnboarding
import Splice.DSO.AmuletPrice
import Splice.DSO.DecentralizedSynchronizer
import Splice.DSO.GovernanceVoter
import Splice.DSO.SvState
import Splice.Schedule
import Splice.Util
Expand Down Expand Up @@ -209,6 +210,9 @@ data DsoRules_RequestVoteResult = DsoRules_RequestVoteResult with
data DsoRules_CastVoteResult = DsoRules_CastVoteResult with
voteRequest : ContractId VoteRequest

data DsoRules_CastGovernanceVoteResult = DsoRules_CastGovernanceVoteResult with
voteRequest : ContractId VoteRequest

data DsoRules_UpdateAmuletPriceVoteResult = DsoRules_UpdateAmuletPriceVoteResult with
amuletPriceVote : ContractId AmuletPriceVote

Expand Down Expand Up @@ -820,6 +824,44 @@ template DsoRules with
trackingCid = Some (fromOptional requestCid request.trackingCid)
return DsoRules_CastVoteResult with ..

-- Note that this choice can be used to both cast the initial governance-voter
-- vote, and update a vote for the represented SV.
nonconsuming choice DsoRules_CastGovernanceVote : DsoRules_CastGovernanceVoteResult
with
requestCid : ContractId VoteRequest
bindingCid : ContractId SvGovernanceVoter
vote : Vote
controller vote.castBy
do
-- validate vote parameters
requireWellformedVote config vote
binding <- fetch bindingCid
require "Binding dso must match rules dso" (binding.dso == dso)
require "Vote SV must match binding SV" (vote.sv == binding.sv)
require "Vote signer must match binding governance voter" (vote.castBy == binding.governanceVoter)
require "Vote signer role must be governance voter" (vote.castByRole == VCR_GovernanceVoter)
voterName <- case Map.lookup binding.sv svs of
None -> fail "Voter is not an SV"
Some info -> pure info.name
-- validate and archive request
request <- fetchAndArchive (ForDso with dso) requestCid
require "Action is not governance-voter eligible" (isGovernanceVoterAction request.action)
-- rate limit casting of votes by the same represented SV to avoid blocking progress
enforceCooldown ("voteCooldownTime for " <> partyToText binding.sv) (getVoteCooldownTime config) $ do
pastVote <- Map.lookup voterName request.votes
pastVote.optCastAt
-- store vote in the represented SV's vote slot
now <- getTime
let recordedVote = vote with
sv = binding.sv
castBy = binding.governanceVoter
castByRole = VCR_GovernanceVoter
optCastAt = Some now
voteRequest <- create request with
votes = Map.insert voterName recordedVote request.votes
trackingCid = Some (fromOptional requestCid request.trackingCid)
return DsoRules_CastGovernanceVoteResult with ..

-- We expect the TxHistory log of the SV app to store the choice argument together
-- with its result for historical reference.
--
Expand Down
4 changes: 4 additions & 0 deletions docs/src/sv_operator/sv_governance_voter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ keeps that invariant outside the template key space so reviewers can decide
whether workflow validation is sufficient or whether a separate registry pattern
is needed before an upstream design is finalized.

Governance-voter vote submission fetches the binding by contract ID, validates
the signer against the binding, checks the action allowlist, and writes the vote
into the represented SV's vote slot.

The first contract slice uses a hardcoded Daml allowlist for governance-voter
eligible actions. New ``ActionRequiringConfirmation`` constructors are rejected
by default until reviewed and added deliberately. The proposed allowlist is
Expand Down