handle proposal creation

This commit is contained in:
Hongrui Fang 2022-04-20 12:47:59 +08:00 committed by fanghr
parent a970c0586f
commit ff227bcbf5
2 changed files with 143 additions and 22 deletions

View file

@ -35,16 +35,22 @@ import Generics.SOP (Generic, I (I))
import Agora.AuthorityToken (authorityTokensValidIn) import Agora.AuthorityToken (authorityTokensValidIn)
import Agora.Proposal ( import Agora.Proposal (
PProposalDatum,
PProposalId, PProposalId,
PProposalStatus (PDraft),
PProposalThresholds, PProposalThresholds,
Proposal (..),
ProposalId, ProposalId,
ProposalThresholds, ProposalThresholds,
pnextProposalId, pnextProposalId,
proposalPolicy,
proposalValidator,
) )
import Agora.Utils ( import Agora.Utils (
allInputs, allInputs,
findOutputsToAddress, findOutputsToAddress,
findTxOutDatum, hasOnlyOneTokenOfCurrencySymbol,
mustFindDatum',
passert, passert,
passetClassValueOf, passetClassValueOf,
passetClassValueOf', passetClassValueOf',
@ -66,6 +72,8 @@ import Plutarch.Api.V1 (
PValue, PValue,
mintingPolicySymbol, mintingPolicySymbol,
mkMintingPolicy, mkMintingPolicy,
mkValidator,
validatorHash,
) )
import Plutarch.Api.V1.Extra (pownMintValue) import Plutarch.Api.V1.Extra (pownMintValue)
import Plutarch.DataRepr ( import Plutarch.DataRepr (
@ -79,8 +87,20 @@ import Plutarch.Unsafe (punsafeCoerce)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Plutus.V1.Ledger.Api (MintingPolicy, TxOutRef) import Plutarch.Builtin (pforgetData)
import Plutus.V1.Ledger.Value (AssetClass (..), CurrencySymbol, TokenName (..)) import Plutus.V1.Ledger.Api (
Address (Address),
Credential (ScriptCredential),
CurrencySymbol (..),
MintingPolicy,
TxOutRef,
Validator,
ValidatorHash,
)
import Plutus.V1.Ledger.Value (
AssetClass (..),
TokenName (..),
)
import PlutusTx qualified import PlutusTx qualified
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -210,8 +230,10 @@ governorPolicy params =
- The utxo which holds the state token, has a well well-formed 'GovernorDatum' datum. - The utxo which holds the state token, has a well well-formed 'GovernorDatum' datum.
For 'CreateProposal' redeemer, it will check: For 'CreateProposal' redeemer, it will check:
- Exactly one proposal token is minted. - Exactly one proposal state token is minted.
- The datum which is corresponding to the proposal token must be correct. - Exactly one utxo should be sent to the proposal validator.
- The utxo must contain the proposal state token.
- The datum of said utxo must be correct.
- Proposal id in the governor datum must be advanced. - Proposal id in the governor datum must be advanced.
TODO: PMintGATs TODO: PMintGATs
@ -223,7 +245,7 @@ governorPolicy params =
governorValidator :: Governor -> ClosedTerm PValidator governorValidator :: Governor -> ClosedTerm PValidator
governorValidator params = governorValidator params =
plam $ \datum' redeemer' ctx' -> P.do plam $ \datum' redeemer' ctx' -> P.do
-- TODO: use `PTryFrom` -- TODO: use ptryFrom
redeemer <- pmatch $ pfromData @PGovernorRedeemer $ punsafeCoerce redeemer' redeemer <- pmatch $ pfromData @PGovernorRedeemer $ punsafeCoerce redeemer'
ctx <- pletFields @'["txInfo", "purpose"] ctx' ctx <- pletFields @'["txInfo", "purpose"] ctx'
@ -233,27 +255,30 @@ governorValidator params =
let txOutRef = pfromData txOutRef' let txOutRef = pfromData txOutRef'
PJust ((pfield @"resolved" #) -> ownInput') <- pmatch $ pfindTxInByTxOutRef # txOutRef # txInfo PJust ((pfield @"resolved" #) -> ownInput') <- pmatch $ pfindTxInByTxOutRef # txOutRef # txInfo
ownInput <- pletFields @'["address", "value", "datumHash"] ownInput' ownInput <- pletFields @'["address", "value"] ownInput'
let selfAddress = pfromData $ ownInput.address let selfAddress = pfromData $ ownInput.address
PJust oldDatum'' <- pmatch $ findTxOutDatum # txInfo # ownInput' -- TODO: use ptryFrom
oldDatum' <- plet $ pto oldDatum'' let oldParams' = pfromData @PGovernorDatum $ punsafeCoerce datum'
let oldParams' = pfromData @PGovernorDatum $ punsafeCoerce oldDatum'
oldParams <- pletFields @'["proposalThresholds", "nextProposalId"] oldParams' oldParams <- pletFields @'["proposalThresholds", "nextProposalId"] oldParams'
let ownInputDatumNFTAmount = stateTokenValueOf # ownInput.value let ownInputDatumNFTAmount = stateTokenValueOf # ownInput.value
passert "Own input should have exactly one state token" $ ownInputDatumNFTAmount #== 1 passert "Own input should have exactly one state token" $
ownInputDatumNFTAmount #== 1
ownOutputs <- plet $ findOutputsToAddress # txInfo # selfAddress ownOutputs <- plet $ findOutputsToAddress # txInfo # selfAddress
passert "Exactly one utxo should be sent to the governor" $ plength # ownOutputs #== 1 passert "Exactly one utxo should be sent to the governor" $
plength # ownOutputs #== 1
ownOutput <- pletFields @'["value", "datumHash"] $ phead # ownOutputs ownOutput <- pletFields @'["value", "datumHash"] $ phead # ownOutputs
let ownOuputDatumNFTAmount = stateTokenValueOf # ownOutput.value let ownOuputDatumNFTAmount = stateTokenValueOf # ownOutput.value
passert "State token should stay at governor's address" $ ownOuputDatumNFTAmount #== 1 passert "State token should stay at governor's address" $
passert "Output utxo to governor should have datum" $ pisDJust # ownOutput.datumHash ownOuputDatumNFTAmount #== 1
passert "Output utxo to governor should have datum" $
pisDJust # ownOutput.datumHash
-- TODO: use `PTryFrom` and reject bad datum -- TODO: use `PTryFrom` and reject bad datum
let newDatum' = pfromData @PGovernorDatum $ punsafeCoerce datum' newDatum' <- plet $ mustFindDatum' @PGovernorDatum # ownOutput.datumHash # ctx.txInfo
newParams <- pletFields @'["proposalThresholds", "nextProposalId"] newDatum' newParams <- pletFields @'["proposalThresholds", "nextProposalId"] newDatum'
mint <- plet $ pfromData $ pfield @"mint" # txInfo mint <- plet $ pfromData $ pfield @"mint" # txInfo
@ -261,18 +286,59 @@ governorValidator params =
case redeemer of case redeemer of
PCreateProposal _ -> P.do PCreateProposal _ -> P.do
-- check that nothing is minted
passert "Nothing should be minted" $ plength # mint' #== 0
-- check that proposal is advanced -- check that proposal is advanced
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: waiting for impl of proposal -- check that exactly one proposal token is minted
pps <- plet $ pconstant proposalSymbol
passert "Exactly one proposal token must be minted" $
hasOnlyOneTokenOfCurrencySymbol # pps # mint
outputs <- plet $ findOutputsToAddress # ctx.txInfo # pconstant proposalValidatorAddress
passert "Exactly one utxo should be sent to the proposal validator" $
plength # outputs #== 1
output <- pletFields @'["value", "datumHash"] $ phead # outputs
passert "The proposal state token must be sent to the proposal validator" $
psymbolValueOf # pconstant proposalSymbol # output.value #== 1
passert "The utxo paid to the proposal validator must have datum" $
pisDJust # output.datumHash
let proposalDatum' =
mustFindDatum' @PProposalDatum
# output.datumHash
# ctx.txInfo
proposalParams <-
pletFields
@'["id", "status", "cosigners", "thresholds", "votes"]
proposalDatum'
passert "Invalid proposal id in proposal parameters" $
proposalParams.id #== oldParams.nextProposalId
passert "Invalid thresholds in proposal parameters" $
proposalParams.thresholds #== oldParams.proposalThresholds
passert "Initial proposal votes should be zero" $
pnull #$ pto $ pto $ pfromData proposalParams.votes
passert "Initial proposal status should be Draft" $ P.do
s <- pmatch $ proposalParams.status
case s of
PDraft _ -> pconstant True
_ -> pconstant False
passert "Initial proposal cosigners should be empty" $
pnull #$ pfromData proposalParams.cosigners
ptraceError "Not implemented yet" ptraceError "Not implemented yet"
PMintGATs _ -> P.do PMintGATs _ -> P.do
-- check datum is not changed -- check datum is not changed
passert "Datum should not be changed" $ oldDatum' #== datum' passert "Datum should not be changed" $
(pforgetData $ pdata newDatum') #== datum'
-- check exactly one(?) authority token is minted -- check exactly one(?) authority token is minted
@ -281,10 +347,12 @@ governorValidator params =
PMutateGovernor _ -> P.do PMutateGovernor _ -> P.do
-- check that input has exactly one GAT and will be burnt -- check that input has exactly one GAT and will be burnt
let gatAmount = psymbolValueOf # gatS # mint let gatAmount = psymbolValueOf # gatS # mint
passert "One GAT should be burnt" $ gatAmount #== -1 passert "One GAT should be burnt" $
gatAmount #== -1
-- nothing should be minted/burnt other than GAT -- nothing should be minted/burnt other than GAT
passert "No token should be minted/burnt other than GAT" $ plength # mint' #== 1 passert "No token should be minted/burnt other than GAT" $
plength # mint' #== 1
-- check that GAT is tagged by the address -- check that GAT is tagged by the address
passert "all input GATs are valid" $ passert "all input GATs are valid" $
@ -296,6 +364,27 @@ governorValidator params =
stateTokenAssetClass :: AssetClass stateTokenAssetClass :: AssetClass
stateTokenAssetClass = governorStateTokenAssetClass params stateTokenAssetClass = governorStateTokenAssetClass params
proposalParams :: Proposal
proposalParams =
Proposal
{ governorSTAssetClass = stateTokenAssetClass
}
proposalSymbol :: CurrencySymbol
proposalSymbol = mintingPolicySymbol policy
where
policy :: MintingPolicy
policy = mkMintingPolicy $ proposalPolicy proposalParams
proposalValidatorAddress :: Address
proposalValidatorAddress = Address (ScriptCredential hash) Nothing
where
hash :: ValidatorHash
hash = validatorHash validator
validator :: Validator
validator = mkValidator $ proposalValidator proposalParams
stateTokenValueOf :: Term s (PValue :--> PInteger) stateTokenValueOf :: Term s (PValue :--> PInteger)
stateTokenValueOf = passetClassValueOf' stateTokenAssetClass stateTokenValueOf = passetClassValueOf' stateTokenAssetClass

View file

@ -44,6 +44,9 @@ module Agora.Utils (
validatorHashToTokenName, validatorHashToTokenName,
pvalidatorHashToTokenName, pvalidatorHashToTokenName,
getMintingPolicySymbol, getMintingPolicySymbol,
hasOnlyOneTokenOfAssetClass',
hasOnlyOneTokenOfCurrencySymbol,
mustFindDatum',
) where ) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -518,3 +521,32 @@ pvalidatorHashToTokenName vh = pcon (PTokenName (pto vh))
-- | Get the CurrencySymbol of a PMintingPolicy. -- | Get the CurrencySymbol of a PMintingPolicy.
getMintingPolicySymbol :: ClosedTerm PMintingPolicy -> CurrencySymbol getMintingPolicySymbol :: ClosedTerm PMintingPolicy -> CurrencySymbol
getMintingPolicySymbol v = mintingPolicySymbol $ mkMintingPolicy v getMintingPolicySymbol v = mintingPolicySymbol $ mkMintingPolicy v
hasOnlyOneTokenOfAssetClass' :: AssetClass -> Term s (PValue :--> PBool)
hasOnlyOneTokenOfAssetClass' ac@(AssetClass (as, _)) = phoistAcyclic $
plam $ \vs -> P.do
let ps = pconstant as
psymbolValueOf # ps # vs #== 1
#&& passetClassValueOf' ac # vs #== 1
#&& (plength #$ pto $ pto $ pto vs) #== 1
hasOnlyOneTokenOfCurrencySymbol :: Term s (PCurrencySymbol :--> PValue :--> PBool)
hasOnlyOneTokenOfCurrencySymbol = phoistAcyclic $
plam $ \cs vs -> P.do
psymbolValueOf # cs # vs #== 1
#&& (plength #$ pto $ pto $ pto vs) #== 1
{- Find datum, in an unsafe manner.
FIXME: reimplement using 'ptryFrom'.
-}
mustFindDatum' ::
forall (datum :: PType).
PIsData datum =>
forall s. Term s (PMaybeData PDatumHash :--> PTxInfo :--> datum)
mustFindDatum' = phoistAcyclic $
plam $ \mdh info -> P.do
PDJust ((pfield @"_0" #) -> dh) <- pmatch mdh
PJust dt <- pmatch $ pfindDatum # dh # info
pfromData $ punsafeCoerce dt