agora/agora-specs/Sample/Governor/Mutate.hs
2022-12-08 17:28:26 +01:00

286 lines
7.4 KiB
Haskell

module Sample.Governor.Mutate (
-- * Testing Utilities
GovernorOutputDatumValidity (..),
GATValidity (..),
GovernorParameters (..),
MockEffectParameters (..),
ParameterBundle (..),
-- * Testing Utilities
Validity (..),
mutate,
mkTestCase,
-- * Parameters Bundles
totallyValidBundle,
invalidBundles,
) where
import Agora.Governor (GovernorDatum (..), GovernorRedeemer (MutateGovernor))
import Agora.Proposal (ProposalId (ProposalId), ProposalThresholds (..))
import Data.Default (def)
import Data.Map ((!))
import Data.Text qualified as T
import Plutarch (Script)
import Plutarch.Api.V2 (PMintingPolicy, scriptHash)
import Plutarch.Context (
input,
mint,
output,
pubKey,
script,
withDatum,
withRef,
withValue,
)
import Plutarch.Extra.AssetClass (assetClassValue)
import Plutarch.Extra.ScriptContext (scriptHashToTokenName)
import PlutusLedgerApi.V1.Value qualified as Value
import PlutusLedgerApi.V2 (
Data,
ScriptHash,
TxOutRef (TxOutRef),
Value,
toData,
)
import Sample.Shared (
agoraScripts,
authorityTokenSymbol,
governorAssetClass,
governorScriptHash,
governorValidator,
minAda,
)
import Test.Specification (SpecificationTree, testValidator)
import Test.Util (
CombinableBuilder,
mkSpending,
pubKeyHashes,
sortValue,
)
--------------------------------------------------------------------------------
-- | Represent the validity property of the governor output datum.
data GovernorOutputDatumValidity
= DatumValid
| ValueInvalid
| WrongType
| NoDatum
deriving stock (Bounded, Enum)
-- | Represent the validity property of the authority token UTxO.
data GATValidity
= GATValid
| WrongTag
| NoGAT
deriving stock (Bounded, Enum)
data GovernorParameters = GovernorParameters
{ governorOutputDatumValidity :: GovernorOutputDatumValidity
, stealGST :: Bool
-- ^ Send the GST to somewhere else other than the govenor validator.
}
data MockEffectParameters = MockEffectParameters
{ gatValidity :: GATValidity
, burnGAT :: Bool
-- ^ Whether to burn the GAT in the transaction or not.
}
data ParameterBundle = ParameterBundle
{ governorParameters :: GovernorParameters
, mockEffectParameters :: MockEffectParameters
}
newtype Validity = Validity {forGovernorValidator :: Bool}
--------------------------------------------------------------------------------
governorInputDatum :: GovernorDatum
governorInputDatum =
GovernorDatum
{ proposalThresholds = def
, nextProposalId = ProposalId 0
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
, maximumCreatedProposalsPerStake = 3
}
mkGovernorOutputDatum ::
GovernorOutputDatumValidity ->
Maybe Data
mkGovernorOutputDatum DatumValid =
Just $
toData $
governorInputDatum
{ maximumCreatedProposalsPerStake = 4
}
mkGovernorOutputDatum ValueInvalid =
let invalidProposalThresholds =
ProposalThresholds
{ execute = -1
, create = -1
, toVoting = -1
, vote = -1
, cosign = -1
}
in Just $
toData $
governorInputDatum
{ proposalThresholds =
invalidProposalThresholds
}
mkGovernorOutputDatum WrongType = Just $ toData ()
mkGovernorOutputDatum NoDatum = Nothing
governorRef :: TxOutRef
governorRef =
TxOutRef
"6cce6dfbb697f9e2c4fe9786bb576eb7bd6cbcf7801a4ba13d596006c2d5b957"
1
governorRedeemer :: GovernorRedeemer
governorRedeemer = MutateGovernor
mkGovernorBuilder :: forall b. CombinableBuilder b => GovernorParameters -> b
mkGovernorBuilder ps =
let gst = assetClassValue governorAssetClass 1
value = sortValue $ gst <> minAda
gstOutput =
if ps.stealGST
then pubKey $ head pubKeyHashes
else script governorScriptHash
withGSTDatum =
maybe mempty withDatum $
mkGovernorOutputDatum ps.governorOutputDatumValidity
in mconcat
[ input $
mconcat
[ script governorScriptHash
, withDatum governorInputDatum
, withValue value
, withRef governorRef
]
, output $
mconcat
[ gstOutput
, withGSTDatum
, withValue value
]
]
--------------------------------------------------------------------------------
mockEffectValidator :: Script
mockEffectValidator = agoraScripts ! "agora:noOpValidator"
mockEffectScriptHash :: ScriptHash
mockEffectScriptHash = scriptHash mockEffectValidator
mockAuthScript :: ClosedTerm PMintingPolicy
mockAuthScript = plam $ \_ _ -> popaque $ pcon PUnit
mockAuthScriptHash :: ScriptHash
mockAuthScriptHash =
scriptHash . either (error . T.unpack) id $ compile def mockAuthScript
mkGATValue :: GATValidity -> Integer -> Value
mkGATValue NoGAT _ = mempty
mkGATValue v q =
let authScript = case v of
GATValid -> mockAuthScriptHash
WrongTag -> ""
in Value.singleton
authorityTokenSymbol
(scriptHashToTokenName authScript)
q
mkMockEffectBuilder :: forall b. CombinableBuilder b => MockEffectParameters -> b
mkMockEffectBuilder ps =
let mkGATValue' = mkGATValue ps.gatValidity
inputValue = mkGATValue' 1
outputValue = inputValue <> burnt
burnt =
if ps.burnGAT
then mkGATValue' (-1)
else mempty
in mconcat
[ mint burnt
, input $
mconcat
[ script mockEffectScriptHash
, withValue inputValue
]
, output $
mconcat
[ script mockEffectScriptHash
, withValue outputValue
]
]
--------------------------------------------------------------------------------
mutate :: forall b. CombinableBuilder b => ParameterBundle -> b
mutate pb =
mconcat
[ mkGovernorBuilder pb.governorParameters
, mkMockEffectBuilder pb.mockEffectParameters
]
--------------------------------------------------------------------------------
-- | Run the governor to test the mutation functionality.
mkTestCase :: String -> ParameterBundle -> Validity -> SpecificationTree
mkTestCase name pb (Validity forGov) =
testValidator
forGov
name
governorValidator
governorInputDatum
governorRedeemer
(mkSpending mutate pb governorRef)
--------------------------------------------------------------------------------
-- | The only one valid combination of all the parameters.
totallyValidBundle :: ParameterBundle
totallyValidBundle =
ParameterBundle
{ governorParameters =
GovernorParameters
{ governorOutputDatumValidity = DatumValid
, stealGST = False
}
, mockEffectParameters =
MockEffectParameters
{ gatValidity = GATValid
, burnGAT = True
}
}
--------------------------------------------------------------------------------
{- | All the invalid combination of the parameters.
TODO: use 'Gen'?
-}
invalidBundles :: [ParameterBundle]
invalidBundles = do
gdv <- enumFrom ValueInvalid
sg <- [True, False]
gtv <- enumFrom WrongTag
bgt <- [True, False]
pure $
ParameterBundle
{ governorParameters =
GovernorParameters
{ governorOutputDatumValidity = gdv
, stealGST = sg
}
, mockEffectParameters =
MockEffectParameters
{ gatValidity = gtv
, burnGAT = bgt
}
}