rewrite the doc for governor components
... also explain what GST is
This commit is contained in:
parent
c6cdb3ac31
commit
9e85a7a004
2 changed files with 112 additions and 40 deletions
|
|
@ -159,7 +159,10 @@ test-suite agora-test
|
||||||
hs-source-dirs: agora-test
|
hs-source-dirs: agora-test
|
||||||
other-modules:
|
other-modules:
|
||||||
Spec.AuthorityToken
|
Spec.AuthorityToken
|
||||||
|
<<<<<<< HEAD
|
||||||
Spec.Effect.TreasuryWithdrawal
|
Spec.Effect.TreasuryWithdrawal
|
||||||
|
=======
|
||||||
|
>>>>>>> 0f1b4f3 (rewrite the doc for governor components)
|
||||||
Spec.Model.MultiSig
|
Spec.Model.MultiSig
|
||||||
Spec.Proposal
|
Spec.Proposal
|
||||||
Spec.Sample.Effect.TreasuryWithdrawal
|
Spec.Sample.Effect.TreasuryWithdrawal
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,15 @@
|
||||||
|
|
||||||
{- |
|
{- |
|
||||||
Module : Agora.Governor
|
Module : Agora.Governor
|
||||||
Maintainer : emi@haskell.fyi
|
Maintainer : chfanghr@gmail.com
|
||||||
Description: Governor entity scripts acting as authority of entire system.
|
Description: Governor entity scripts acting as authority of entire system.
|
||||||
|
|
||||||
Governor entity scripts acting as authority of entire system.
|
Governor entity scripts acting as authority of entire system.
|
||||||
-}
|
-}
|
||||||
module Agora.Governor (
|
module Agora.Governor (
|
||||||
|
-- * GST
|
||||||
|
-- $gst
|
||||||
|
|
||||||
-- * Haskell-land
|
-- * Haskell-land
|
||||||
GovernorDatum (..),
|
GovernorDatum (..),
|
||||||
GovernorRedeemer (..),
|
GovernorRedeemer (..),
|
||||||
|
|
@ -90,17 +93,17 @@ import Plutarch.Api.V1 (
|
||||||
import Plutarch.Api.V1.Extra (
|
import Plutarch.Api.V1.Extra (
|
||||||
pownMintValue,
|
pownMintValue,
|
||||||
)
|
)
|
||||||
|
import Plutarch.Builtin (pforgetData)
|
||||||
import Plutarch.DataRepr (
|
import Plutarch.DataRepr (
|
||||||
DerivePConstantViaData (..),
|
DerivePConstantViaData (..),
|
||||||
PDataFields,
|
PDataFields,
|
||||||
PIsDataReprInstances (PIsDataReprInstances),
|
PIsDataReprInstances (PIsDataReprInstances),
|
||||||
)
|
)
|
||||||
import Plutarch.Lift (PUnsafeLiftDecl (..))
|
import Plutarch.Lift (PUnsafeLiftDecl (..))
|
||||||
import Plutarch.Monadic qualified as P
|
|
||||||
import Plutarch.Unsafe (punsafeCoerce)
|
|
||||||
import Plutarch.Builtin (pforgetData)
|
|
||||||
import Plutarch.Map.Extra (plookup, plookup')
|
import Plutarch.Map.Extra (plookup, plookup')
|
||||||
|
import Plutarch.Monadic qualified as P
|
||||||
import Plutarch.SafeMoney (puntag)
|
import Plutarch.SafeMoney (puntag)
|
||||||
|
import Plutarch.Unsafe (punsafeCoerce)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -119,7 +122,17 @@ import PlutusTx qualified
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- | Datum for the Governor script.
|
{- $gst
|
||||||
|
Governance state token, aka. GST, it's a NFT that identifies an UTXO that carries the state datum of the Governance script.
|
||||||
|
|
||||||
|
This token is minted by a one-shot monetary policy 'governorPolicy', meaning that the token has guaranteed uniqueness.
|
||||||
|
|
||||||
|
The 'governorValidator' ensures that exactly one GST stays at the address of itself forever.
|
||||||
|
-}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | State datum for the Governor script.
|
||||||
data GovernorDatum = GovernorDatum
|
data GovernorDatum = GovernorDatum
|
||||||
{ proposalThresholds :: ProposalThresholds
|
{ proposalThresholds :: ProposalThresholds
|
||||||
-- ^ Gets copied over upon creation of a 'Agora.Proposal.ProposalDatum'.
|
-- ^ Gets copied over upon creation of a 'Agora.Proposal.ProposalDatum'.
|
||||||
|
|
@ -145,25 +158,22 @@ data GovernorRedeemer
|
||||||
-- and allows minting GATs for each effect script.
|
-- and allows minting GATs for each effect script.
|
||||||
MintGATs
|
MintGATs
|
||||||
| -- | Allows effects to mutate the parameters.
|
| -- | Allows effects to mutate the parameters.
|
||||||
MutateMutateGovernor
|
MutateGovernor
|
||||||
deriving stock (Show, GHC.Generic)
|
deriving stock (Show, GHC.Generic)
|
||||||
|
|
||||||
PlutusTx.makeIsDataIndexed
|
PlutusTx.makeIsDataIndexed
|
||||||
''GovernorRedeemer
|
''GovernorRedeemer
|
||||||
[ ('CreateProposal, 0)
|
[ ('CreateProposal, 0)
|
||||||
, ('MintGATs, 1)
|
, ('MintGATs, 1)
|
||||||
, ('MutateMutateGovernor, 2)
|
, ('MutateGovernor, 2)
|
||||||
]
|
]
|
||||||
|
|
||||||
{- | Parameters for creating Governor scripts.
|
-- | Parameters for creating Governor scripts.
|
||||||
|
|
||||||
Governance State Token, aka GST, is an NFT which idetifies the governance state utxo.
|
|
||||||
-}
|
|
||||||
data Governor = Governor
|
data Governor = Governor
|
||||||
{ gstORef :: TxOutRef
|
{ gstORef :: TxOutRef
|
||||||
-- ^ Referenced utxo will be spent to mint the GST
|
-- ^ Referenced utxo will be spent to mint the GST.
|
||||||
, gstName :: TokenName
|
, gstName :: TokenName
|
||||||
-- ^ Name of the GST token
|
-- ^ Name of the GST token.
|
||||||
}
|
}
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
@ -206,23 +216,27 @@ deriving via (DerivePConstantViaData GovernorRedeemer PGovernorRedeemer) instanc
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
{- | Policy for Governors.
|
{- | Policy for minting GSTs.
|
||||||
This policy mints a GST. It perform the following checks:
|
|
||||||
|
|
||||||
- The utxo specified in the Governor parameter is spent.
|
This policy perform the following checks:
|
||||||
- Only one token is minted.
|
|
||||||
|
- The UTXO referenced in the parameter is spent in the transaction.
|
||||||
|
- Exactly one GST is minted.
|
||||||
- Ensure the token name is 'gstName'.
|
- Ensure the token name is 'gstName'.
|
||||||
|
|
||||||
|
NOTE: It's user's responsibility to make sure the token is sent to the corresponding governor validator.
|
||||||
|
We /can't/ really check this in the policy, otherwise we create a cyclic reference issue.
|
||||||
-}
|
-}
|
||||||
governorPolicy :: Governor -> ClosedTerm PMintingPolicy
|
governorPolicy :: Governor -> ClosedTerm PMintingPolicy
|
||||||
governorPolicy gov =
|
governorPolicy gov =
|
||||||
plam $ \_ ctx' -> P.do
|
plam $ \_ ctx' -> P.do
|
||||||
ctx <- pletFields @'["txInfo", "purpose"] ctx'
|
|
||||||
let oref = pconstant gov.gstORef
|
let oref = pconstant gov.gstORef
|
||||||
ownSymbol = pownCurrencySymbol # ctx'
|
ownSymbol = pownCurrencySymbol # ctx'
|
||||||
|
|
||||||
mintValue <- plet $ pownMintValue # ctx'
|
mintValue <- plet $ pownMintValue # ctx'
|
||||||
|
|
||||||
passert "Referenced utxo should be spent" $ pisUxtoSpent # oref # ctx.txInfo
|
passert "Referenced utxo should be spent" $
|
||||||
|
pisUxtoSpent # oref #$ pfield @"txInfo" # ctx'
|
||||||
|
|
||||||
passert "Exactly one token should be minted" $
|
passert "Exactly one token should be minted" $
|
||||||
psymbolValueOf # ownSymbol # mintValue #== 1
|
psymbolValueOf # ownSymbol # mintValue #== 1
|
||||||
|
|
@ -232,29 +246,78 @@ governorPolicy gov =
|
||||||
|
|
||||||
{- | Validator for Governors.
|
{- | Validator for Governors.
|
||||||
|
|
||||||
No matter what redeemer it receives, it will always check:
|
== Common checks
|
||||||
- The utxo which has the GST must be spent.
|
|
||||||
- The GST always stays at the script address.
|
|
||||||
- The state utxo has a valid 'GovernorDatum' datum.
|
|
||||||
|
|
||||||
For 'CreateProposal' redeemers, it will check:
|
The validator always ensures:
|
||||||
- Governance state 'nextProposalId' must be advanced.
|
|
||||||
- Exactly one proposal state token is minted, or rather, only one proposal is created.
|
|
||||||
- Exactly one utxo should be sent to the proposal validator. This utxo must contain the proposal state token, and has a valid datum of type 'ProposalDatum'.
|
|
||||||
- Said proposal copies its id and thresholds from the governor, is in draft state, and has zero votes.
|
|
||||||
|
|
||||||
For 'MintGATs' redeemers, it will check:
|
- The UTXO which holds the GST must be spent.
|
||||||
- Governance state datum is not changed.
|
- The GST always stays at the validator's address.
|
||||||
- Exactly one proposal(the input proposal) is being processed.
|
- The new state UTXO has a valid datum of type 'GovernorDatum'.
|
||||||
- The input proposal must be in executable state and have required amount of votes.
|
|
||||||
- An appropriate effect group is selected to be executed.
|
|
||||||
- A valid GAT is minted and sent to every effect,
|
|
||||||
- Exactly one utxo should be sent back to the proposal validator. This utxo must contain the proposal state token, and also has a valid datum of type 'ProposalDatum'(the output proposal).
|
|
||||||
- Said output proposal's status should be `Finished`. Other than that, nothing should be changed compare to the input proposal.
|
|
||||||
|
|
||||||
For 'MutateGovernors' redeemers, it will check:
|
== Creating a Proposal
|
||||||
- Exactly one GAT is burnt.
|
|
||||||
- Said GAT must be valid.
|
When the redeemer is 'CreateProposal', the script will check:
|
||||||
|
|
||||||
|
- For governor's state datum:
|
||||||
|
|
||||||
|
* 'nextProposalId' is advanced.
|
||||||
|
* Nothing is changed other that that.
|
||||||
|
|
||||||
|
- Exactly one new proposal state token is minted.
|
||||||
|
- Exactly one UTXO is sent to the proposal validator, this UTXO must:
|
||||||
|
|
||||||
|
* Hold the newly minted proposal state token.
|
||||||
|
* Have a valid datum of type 'Agora.Proposal.ProposalDatum', the datum must:
|
||||||
|
|
||||||
|
- Copy its id and thresholds from the governor's state.
|
||||||
|
- Have status set to 'Proposal.Draft'.
|
||||||
|
- Have zero votes.
|
||||||
|
- TODO: should we check cosigners?
|
||||||
|
|
||||||
|
== Minting GATs
|
||||||
|
|
||||||
|
When the redeemer is 'MintGATs', the script will check:
|
||||||
|
|
||||||
|
- Governor's state is not changed.
|
||||||
|
- Exactly only one proposal is in the inputs. Let's call this the /input proposal/.
|
||||||
|
- The proposal is in the 'Proposal.Executable' state.
|
||||||
|
|
||||||
|
NOTE: The input proposal is found by looking for the UTXO with a proposal state token in the inputs.
|
||||||
|
|
||||||
|
=== Effect Group Selection
|
||||||
|
|
||||||
|
Currently a proposal can two or more than two options to vote on,
|
||||||
|
meaning that it can conatinas two or more effect groups,
|
||||||
|
according to [#39](https://github.com/Liqwid-Labs/agora/issues/39).
|
||||||
|
|
||||||
|
Either way, the shapes of 'Proposal.votes' and 'Proposal.effects' should be the same.
|
||||||
|
This is checked by 'Proposal.proposalDatumValid'.
|
||||||
|
|
||||||
|
The script will look at the the 'Proposal.votes' to determine which group has the highest votes,
|
||||||
|
said group shoud be executed.
|
||||||
|
|
||||||
|
During the process, minimum votes requirement will also be enforced.
|
||||||
|
|
||||||
|
Next, the script will:
|
||||||
|
|
||||||
|
- Ensure that for every effect in the said effect group,
|
||||||
|
exactly one valid GAT is minted and sent to the effect.
|
||||||
|
- The amount of GAT minted in the transaction should be equal to the number of effects.
|
||||||
|
- A new UTXO is sent to the proposal validator, this UTXO should:
|
||||||
|
|
||||||
|
* Include the one proposal state token.
|
||||||
|
* Have a valid datum of type 'Proposal.ProposalDatum'.
|
||||||
|
This datum should be as same as the one of the input proposal,
|
||||||
|
except its status should be 'Proposal.Finished'.
|
||||||
|
|
||||||
|
== Changing the State
|
||||||
|
|
||||||
|
Redeemer 'MutateGovernor' allows the state datum to be changed by an external effect.
|
||||||
|
|
||||||
|
In this case, the script will check
|
||||||
|
|
||||||
|
- Exactly one GAT is burnt in the transaction.
|
||||||
|
- Said GAT is tagged by the effect.
|
||||||
-}
|
-}
|
||||||
governorValidator :: Governor -> ClosedTerm PValidator
|
governorValidator :: Governor -> ClosedTerm PValidator
|
||||||
governorValidator gov =
|
governorValidator gov =
|
||||||
|
|
@ -301,6 +364,8 @@ governorValidator gov =
|
||||||
passert "Proposal id should be advanced by 1" $
|
passert "Proposal id should be advanced by 1" $
|
||||||
pnextProposalId # oldParams.nextProposalId #== newParams.nextProposalId
|
pnextProposalId # oldParams.nextProposalId #== newParams.nextProposalId
|
||||||
|
|
||||||
|
-- TODO: check other fields of the state datum
|
||||||
|
|
||||||
passert "Exactly one proposal token must be minted" $
|
passert "Exactly one proposal token must be minted" $
|
||||||
hasOnlyOneTokenOfCurrencySymbol # pproposalSymbol # txInfo.mint
|
hasOnlyOneTokenOfCurrencySymbol # pproposalSymbol # txInfo.mint
|
||||||
|
|
||||||
|
|
@ -425,6 +490,8 @@ governorValidator gov =
|
||||||
|
|
||||||
-- TODO: anything else to check here?
|
-- TODO: anything else to check here?
|
||||||
|
|
||||||
|
-- TODO: support more than two effect group.
|
||||||
|
|
||||||
PProposalVotes votes' <- pmatch $ pfromData inputProposalDatum.votes
|
PProposalVotes votes' <- pmatch $ pfromData inputProposalDatum.votes
|
||||||
votes <- plet votes'
|
votes <- plet votes'
|
||||||
|
|
||||||
|
|
@ -524,6 +591,7 @@ governorValidator gov =
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Get the assetclass of GST from governor parameters.
|
||||||
gstAssetClass :: Governor -> AssetClass
|
gstAssetClass :: Governor -> AssetClass
|
||||||
gstAssetClass gov = AssetClass (symbol, gov.gstName)
|
gstAssetClass gov = AssetClass (symbol, gov.gstName)
|
||||||
where
|
where
|
||||||
|
|
@ -533,8 +601,9 @@ gstAssetClass gov = AssetClass (symbol, gov.gstName)
|
||||||
symbol :: CurrencySymbol
|
symbol :: CurrencySymbol
|
||||||
symbol = mintingPolicySymbol policy
|
symbol = mintingPolicySymbol policy
|
||||||
|
|
||||||
|
-- | Get the currency symbol of GAT from governor parameters.
|
||||||
gatSymbol :: Governor -> CurrencySymbol
|
gatSymbol :: Governor -> CurrencySymbol
|
||||||
gatSymbol gov = mintingPolicySymbol policy
|
gatSymbol gov = mintingPolicySymbol policy
|
||||||
where
|
where
|
||||||
at = AuthorityToken $ gstAssetClass gov
|
at = AuthorityToken $ gstAssetClass gov
|
||||||
policy = mkMintingPolicy $ authorityTokenPolicy at
|
policy = mkMintingPolicy $ authorityTokenPolicy at
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue