Merge pull request #157 from Liqwid-Labs/connor/effect-ref-script

Store `ScriptHash`es in the effects
This commit is contained in:
方泓睿 2022-08-17 12:52:37 +08:00 committed by GitHub
commit 367a630cef
12 changed files with 897 additions and 574 deletions

View file

@ -44,6 +44,7 @@ import Agora.Governor (
) )
import Agora.Proposal ( import Agora.Proposal (
ProposalDatum (..), ProposalDatum (..),
ProposalEffectGroup,
ProposalId (ProposalId), ProposalId (ProposalId),
ProposalRedeemer (AdvanceProposal), ProposalRedeemer (AdvanceProposal),
ProposalStatus (..), ProposalStatus (..),
@ -67,10 +68,11 @@ import Agora.Stake (
StakeRedeemer (WitnessStake), StakeRedeemer (WitnessStake),
) )
import Agora.Utils (validatorHashToTokenName) import Agora.Utils (validatorHashToTokenName)
import Control.Applicative (liftA2)
import Control.Monad.State (execState, modify, when) import Control.Monad.State (execState, modify, when)
import Data.Default (def) import Data.Default (def)
import Data.List (sort) import Data.List (singleton, sort)
import Data.Maybe (catMaybes, fromJust) import Data.Maybe (fromJust)
import Data.Tagged (Tagged (..), untag) import Data.Tagged (Tagged (..), untag)
import Plutarch.Context ( import Plutarch.Context (
input, input,
@ -81,6 +83,7 @@ import Plutarch.Context (
timeRange, timeRange,
withDatum, withDatum,
withRef, withRef,
withReferenceScript,
withValue, withValue,
) )
import Plutarch.Lift (PLifted, PUnsafeLiftDecl) import Plutarch.Lift (PLifted, PUnsafeLiftDecl)
@ -91,6 +94,7 @@ import PlutusLedgerApi.V2 (
POSIXTime, POSIXTime,
POSIXTimeRange, POSIXTimeRange,
PubKeyHash, PubKeyHash,
ScriptHash,
TxOutRef (TxOutRef), TxOutRef (TxOutRef),
ValidatorHash, ValidatorHash,
) )
@ -127,6 +131,7 @@ import Test.Util (
mkMinting, mkMinting,
mkSpending, mkSpending,
pubKeyHashes, pubKeyHashes,
scriptHashes,
sortValue, sortValue,
toDatum, toDatum,
updateMap, updateMap,
@ -144,7 +149,7 @@ data ParameterBundle = ParameterBundle
, governorParameters :: Maybe GovernorParameters , governorParameters :: Maybe GovernorParameters
-- ^ Parameters related to GST moving. If set to 'Nothing', the GST won't -- ^ Parameters related to GST moving. If set to 'Nothing', the GST won't
-- be moved, thus the governor validator won't be run in 'mkTestTree'. -- be moved, thus the governor validator won't be run in 'mkTestTree'.
, authorityTokenParameters :: Maybe AuthorityTokenParameters , authorityTokenParameters :: [AuthorityTokenParameters]
-- ^ Parameters related to GAT minting. If set to 'Nothing', no GAT will -- ^ Parameters related to GAT minting. If set to 'Nothing', no GAT will
-- be minted, thus the GAT minting policy won't be run in 'mkTestTree'. -- be minted, thus the GAT minting policy won't be run in 'mkTestTree'.
, transactionTimeRange :: POSIXTimeRange , transactionTimeRange :: POSIXTimeRange
@ -171,10 +176,12 @@ data AuthorityTokenParameters = forall
, PIsData pdatum , PIsData pdatum
) => ) =>
AuthorityTokenParameters AuthorityTokenParameters
{ mintGATsFor :: [ValidatorHash] { mintGATsFor :: ValidatorHash
-- ^ GATs will be minted and sent to the given group of effects. -- ^ GATs will be minted and sent to the given group of effects.
, carryDatum :: Maybe datum , carryDatum :: Maybe datum
-- ^ The datum that GAT UTxOs will be carrying. -- ^ The datum that GAT UTxOs will be carrying.
, carryRefScript :: Maybe ScriptHash
-- ^ The reference script that GAT UTxOs will be carrying.
, invalidTokenName :: Bool , invalidTokenName :: Bool
-- ^ If set to true, GATs won't be tagged by their corresponding effect -- ^ If set to true, GATs won't be tagged by their corresponding effect
-- hashes. -- hashes.
@ -193,7 +200,7 @@ data ProposalParameters = ProposalParameters
-- ^ What status is the proposal advancing from -- ^ What status is the proposal advancing from
, toStatus :: ProposalStatus , toStatus :: ProposalStatus
-- ^ What status is the proposal advancing to -- ^ What status is the proposal advancing to
, effectList :: [AssocMap.Map ValidatorHash DatumHash] , effectList :: [ProposalEffectGroup]
-- ^ The effect groups of the proposal. A neutral effect group is not -- ^ The effect groups of the proposal. A neutral effect group is not
-- required here. -- required here.
, winnerAndVotes :: Maybe (Winner, Integer) , winnerAndVotes :: Maybe (Winner, Integer)
@ -244,7 +251,7 @@ outcomeIdxToResultTag = ResultTag . fromIntegral
-- | Add a neutral effect group and allocate result tags for the effect groups. -- | Add a neutral effect group and allocate result tags for the effect groups.
mkEffects :: mkEffects ::
ProposalParameters -> ProposalParameters ->
AssocMap.Map ResultTag (AssocMap.Map ValidatorHash DatumHash) AssocMap.Map ResultTag ProposalEffectGroup
mkEffects ps = mkEffects ps =
let resultTags = map ResultTag [0 ..] let resultTags = map ResultTag [0 ..]
neutralEffect = AssocMap.empty neutralEffect = AssocMap.empty
@ -492,27 +499,24 @@ mkAuthorityTokenBuilder ::
CombinableBuilder b => CombinableBuilder b =>
AuthorityTokenParameters -> AuthorityTokenParameters ->
b b
mkAuthorityTokenBuilder (AuthorityTokenParameters es mdt invalidTokenName) = mkAuthorityTokenBuilder ps@AuthorityTokenParameters {carryDatum} =
foldMap perEffect es let tn =
where if ps.invalidTokenName
perEffect :: ValidatorHash -> b then ""
perEffect vh = else validatorHashToTokenName ps.mintGATsFor
let tn = ac = AssetClass (authorityTokenSymbol, tn)
if invalidTokenName minted = Value.assetClassValue ac 1
then "" value = sortValue $ minAda <> minted
else validatorHashToTokenName vh in mconcat
ac = AssetClass (authorityTokenSymbol, tn) [ mint minted
minted = Value.assetClassValue ac 1 , output $
value = sortValue $ minAda <> minted mconcat
in mconcat [ script ps.mintGATsFor
[ mint minted , maybe mempty withDatum carryDatum
, output $ , maybe mempty withReferenceScript ps.carryRefScript
mconcat , withValue value
[ script vh ]
, maybe mempty withDatum mdt ]
, withValue value
]
]
-- | The redeemer used while running the authority token policy. -- | The redeemer used while running the authority token policy.
authorityTokenRedeemer :: () authorityTokenRedeemer :: ()
@ -532,7 +536,7 @@ advance pb =
[ mkProposalBuilder pb.proposalParameters [ mkProposalBuilder pb.proposalParameters
, mkStakeBuilder pb.stakeParameters , mkStakeBuilder pb.stakeParameters
, mkBuilderMaybe mkGovernorBuilder pb.governorParameters , mkBuilderMaybe mkGovernorBuilder pb.governorParameters
, mkBuilderMaybe mkAuthorityTokenBuilder pb.authorityTokenParameters , foldMap mkAuthorityTokenBuilder pb.authorityTokenParameters
, timeRange pb.transactionTimeRange , timeRange pb.transactionTimeRange
, maybe mempty signedWith pb.extraSignature , maybe mempty signedWith pb.extraSignature
] ]
@ -548,14 +552,13 @@ mkTestTree ::
Validity -> Validity ->
SpecificationTree SpecificationTree
mkTestTree name pb val = mkTestTree name pb val =
group name $ catMaybes [proposal, stake, governor, authority] group name $ mconcat [proposal, stake, governor, authority]
where where
spend = mkSpending advance pb spend = mkSpending advance pb
mint = mkMinting advance pb
proposal = proposal =
let proposalInputDatum = mkProposalInputDatum pb.proposalParameters let proposalInputDatum = mkProposalInputDatum pb.proposalParameters
in Just $ in singleton $
testValidator testValidator
val.forProposalValidator val.forProposalValidator
"proposal" "proposal"
@ -566,7 +569,7 @@ mkTestTree name pb val =
stake = stake =
let idx = 0 let idx = 0
in Just $ in singleton $
testValidator testValidator
val.forStakeValidator val.forStakeValidator
"stake" "stake"
@ -577,23 +580,27 @@ mkTestTree name pb val =
) )
governor = governor =
testValidator maybe [] singleton $
(fromJust val.forGovernorValidator) testValidator
"governor" (fromJust val.forGovernorValidator)
agoraScripts.compiledGovernorValidator "governor"
governorInputDatum agoraScripts.compiledGovernorValidator
governorRedeemer governorInputDatum
(spend governorRef) governorRedeemer
<$ pb.governorParameters (spend governorRef)
<$ pb.governorParameters
authority = authority = case pb.authorityTokenParameters of
testPolicy [] -> []
(fromJust val.forAuthorityTokenPolicy) _ ->
"authority" singleton
agoraScripts.compiledAuthorityTokenPolicy ( testPolicy
authorityTokenRedeemer (fromJust val.forAuthorityTokenPolicy)
(mint authorityTokenSymbol) "authority"
<$ (pb.authorityTokenParameters) agoraScripts.compiledAuthorityTokenPolicy
authorityTokenRedeemer
(mkMinting advance pb authorityTokenSymbol)
)
{- | Create a test tree that runs a bunch of parameter bundles. These bundles {- | Create a test tree that runs a bunch of parameter bundles. These bundles
should have the same validity. should have the same validity.
@ -725,14 +732,26 @@ dummyDatumHash :: DatumHash
dummyDatumHash = datumHash $ toDatum dummyDatum dummyDatumHash = datumHash $ toDatum dummyDatum
-- | Create given number of effect groups. Each group will have 3 effects. -- | Create given number of effect groups. Each group will have 3 effects.
mkMockEffects :: Int -> [AssocMap.Map ValidatorHash DatumHash] mkMockEffects :: Bool -> Int -> [ProposalEffectGroup]
mkMockEffects = mkMockEffects useRefScript n = effects
flip where
take effectsPerGroup = 3
( AssocMap.fromList
. flip zip (repeat dummyDatumHash) mkRefScripts True = Just <$> scriptHashes
<$> groupsOfN 3 validatorHashes mkRefScripts False = repeat Nothing
) refScripts = mkRefScripts useRefScript
datums = repeat dummyDatumHash
effectMetadata = zip datums refScripts
effectScripts = validatorHashes
effects =
take n $
AssocMap.fromList
<$> groupsOfN
effectsPerGroup
(zip effectScripts effectMetadata)
numberOfVotesThatExceedsTheMinimumRequirement :: Integer numberOfVotesThatExceedsTheMinimumRequirement :: Integer
numberOfVotesThatExceedsTheMinimumRequirement = numberOfVotesThatExceedsTheMinimumRequirement =
@ -767,16 +786,18 @@ defaultWinnerIdx = 0
mkValidToNextStateBundle :: mkValidToNextStateBundle ::
-- | Number of cosigners. -- | Number of cosigners.
Word -> Word ->
-- | Number of effects -- | Number of effects.
Word -> Word ->
-- | Toggle the referenc script in GAT UTXO.
Bool ->
-- | The initial proposal state, should not be 'Finished'. -- | The initial proposal state, should not be 'Finished'.
ProposalStatus -> ProposalStatus ->
ParameterBundle ParameterBundle
mkValidToNextStateBundle _ _ Finished = mkValidToNextStateBundle _ _ _ Finished =
error "Cannot advance from Finished" error "Cannot advance from Finished"
mkValidToNextStateBundle nCosigners nEffects from = mkValidToNextStateBundle nCosigners nEffects refScript from =
let next = getNextState from let next = getNextState from
effects = mkMockEffects $ fromIntegral nEffects effects = mkMockEffects refScript $ fromIntegral nEffects
winner = defaultWinnerIdx winner = defaultWinnerIdx
template = template =
@ -800,7 +821,7 @@ mkValidToNextStateBundle nCosigners nEffects from =
, invalidStakeOutputDatum = False , invalidStakeOutputDatum = False
} }
, governorParameters = Nothing , governorParameters = Nothing
, authorityTokenParameters = Nothing , authorityTokenParameters = []
, transactionTimeRange = mkInTimeTimeRange from , transactionTimeRange = mkInTimeTimeRange from
, extraSignature = Just signer , extraSignature = Just signer
} }
@ -830,18 +851,24 @@ mkValidToNextStateBundle nCosigners nEffects from =
when (from == Locked) $ when (from == Locked) $
modify $ \b -> modify $ \b ->
let aut = let aut =
AuthorityTokenParameters AssocMap.elems $
{ mintGATsFor = AssocMap.keys $ effects !! winner AssocMap.mapWithKey
, carryDatum = Just dummyDatum ( \vh (_, refScript) ->
, invalidTokenName = False AuthorityTokenParameters
} { mintGATsFor = vh
, carryDatum = Just dummyDatum
, carryRefScript = refScript
, invalidTokenName = False
}
)
(effects !! winner)
gov = gov =
GovernorParameters GovernorParameters
{ invalidGovernorOutputDatum = False { invalidGovernorOutputDatum = False
} }
in b in b
{ governorParameters = Just gov { governorParameters = Just gov
, authorityTokenParameters = Just aut , authorityTokenParameters = aut
} }
in execState modifyTemplate template in execState modifyTemplate template
@ -852,11 +879,10 @@ mkValidToNextStateBundles ::
Word -> Word ->
[ParameterBundle] [ParameterBundle]
mkValidToNextStateBundles nCosigners nEffects = mkValidToNextStateBundles nCosigners nEffects =
mkValidToNextStateBundle nCosigners nEffects liftA2
<$> [ Draft (mkValidToNextStateBundle nCosigners nEffects)
, VotingReady [True, False]
, Locked [Draft, VotingReady, Locked]
]
mkValidToFailedStateBundles :: mkValidToFailedStateBundles ::
-- | Number of cosigners -- | Number of cosigners
@ -865,15 +891,14 @@ mkValidToFailedStateBundles ::
Word -> Word ->
[ParameterBundle] [ParameterBundle]
mkValidToFailedStateBundles nCosigners nEffects = mkValidToFailedStateBundles nCosigners nEffects =
mkBundle liftA2
<$> [ Draft mkBundle
, VotingReady [True, False]
, Locked [Draft, VotingReady, Locked]
]
where where
mkBundle from = mkBundle refScript from =
let next = Finished let next = Finished
effects = mkMockEffects $ fromIntegral nEffects effects = mkMockEffects refScript $ fromIntegral nEffects
in ParameterBundle in ParameterBundle
{ proposalParameters = { proposalParameters =
ProposalParameters ProposalParameters
@ -894,7 +919,7 @@ mkValidToFailedStateBundles nCosigners nEffects =
, invalidStakeOutputDatum = False , invalidStakeOutputDatum = False
} }
, governorParameters = Nothing , governorParameters = Nothing
, authorityTokenParameters = Nothing , authorityTokenParameters = []
, transactionTimeRange = mkTooLateTimeRange from , transactionTimeRange = mkTooLateTimeRange from
, extraSignature = Just signer , extraSignature = Just signer
} }
@ -908,14 +933,13 @@ mkFromFinishedBundles ::
Word -> Word ->
[ParameterBundle] [ParameterBundle]
mkFromFinishedBundles nCosigners nEffects = mkFromFinishedBundles nCosigners nEffects =
mkBundle liftA2
<$> [ Draft mkBundle
, VotingReady [True, False]
, Locked [Draft, VotingReady, Locked]
]
where where
mkBundle from = mkBundle refScript from =
let template = mkValidToNextStateBundle nCosigners nEffects from let template = mkValidToNextStateBundle nCosigners nEffects refScript from
in template in template
{ proposalParameters = { proposalParameters =
template.proposalParameters template.proposalParameters
@ -926,24 +950,26 @@ mkFromFinishedBundles nCosigners nEffects =
mkToNextStateTooLateBundles :: Word -> Word -> [ParameterBundle] mkToNextStateTooLateBundles :: Word -> Word -> [ParameterBundle]
mkToNextStateTooLateBundles nCosigners nEffects = mkToNextStateTooLateBundles nCosigners nEffects =
mkBundle liftA2
<$> [ Draft mkBundle
, VotingReady [True, False]
, Locked [Draft, VotingReady, Locked]
]
where where
mkBundle from = mkBundle refScript from =
let template = mkValidToNextStateBundle nCosigners nEffects from let template = mkValidToNextStateBundle nCosigners nEffects refScript from
in template in template
{ transactionTimeRange = mkTooLateTimeRange from { transactionTimeRange = mkTooLateTimeRange from
} }
mkInvalidOutputStakeBundles :: Word -> Word -> [ParameterBundle] mkInvalidOutputStakeBundles :: Word -> Word -> [ParameterBundle]
mkInvalidOutputStakeBundles nCosigners nEffects = mkInvalidOutputStakeBundles nCosigners nEffects =
mkBundle <$> [Draft, VotingReady, Locked] liftA2
mkBundle
[True, False]
[Draft, VotingReady, Locked]
where where
mkBundle from = mkBundle refScript from =
let template = mkValidToNextStateBundle nCosigners nEffects from let template = mkValidToNextStateBundle nCosigners nEffects refScript from
in template in template
{ stakeParameters = { stakeParameters =
template.stakeParameters template.stakeParameters
@ -965,7 +991,7 @@ mkInsufficientCosignsBundle nCosigners nEffects =
insuffcientPerStakeGTs = insuffcientPerStakeGTs =
untag (def :: ProposalThresholds).vote untag (def :: ProposalThresholds).vote
`div` fromIntegral nCosigners - 1 `div` fromIntegral nCosigners - 1
template = mkValidToNextStateBundle nCosigners nEffects Draft template = mkValidToNextStateBundle nCosigners nEffects False Draft
-- * From VotingReady -- * From VotingReady
@ -986,7 +1012,7 @@ mkInsufficientVotesBundle ::
Word -> Word ->
ParameterBundle ParameterBundle
mkInsufficientVotesBundle nCosigners nEffects = mkInsufficientVotesBundle nCosigners nEffects =
mkValidToNextStateBundle nCosigners nEffects VotingReady mkValidToNextStateBundle nCosigners nEffects False VotingReady
`setWinnerAndVotes` Nothing `setWinnerAndVotes` Nothing
mkAmbiguousWinnerBundle :: mkAmbiguousWinnerBundle ::
@ -994,14 +1020,14 @@ mkAmbiguousWinnerBundle ::
Word -> Word ->
ParameterBundle ParameterBundle
mkAmbiguousWinnerBundle nCosigners nEffects = mkAmbiguousWinnerBundle nCosigners nEffects =
mkValidToNextStateBundle nCosigners nEffects VotingReady mkValidToNextStateBundle nCosigners nEffects False VotingReady
`setWinnerAndVotes` Just ambiguousWinnerVotes `setWinnerAndVotes` Just ambiguousWinnerVotes
-- * From Locked -- * From Locked
mkValidFromLockedBundle :: Word -> Word -> ParameterBundle mkValidFromLockedBundle :: Word -> Word -> ParameterBundle
mkValidFromLockedBundle nCosigners nEffects = mkValidFromLockedBundle nCosigners nEffects =
mkValidToNextStateBundle nCosigners nEffects Locked mkValidToNextStateBundle nCosigners nEffects False Locked
mkMintGATsForWrongEffectsBundle :: mkMintGATsForWrongEffectsBundle ::
Word -> Word ->
@ -1010,17 +1036,11 @@ mkMintGATsForWrongEffectsBundle ::
mkMintGATsForWrongEffectsBundle nCosigners nEffects = mkMintGATsForWrongEffectsBundle nCosigners nEffects =
template template
{ authorityTokenParameters = { authorityTokenParameters =
( \aut -> take 4 $
aut zipWith
{ mintGATsFor = (\a i -> a {mintGATsFor = validatorHashes !! i})
[ validatorHashes !! 1 template.authorityTokenParameters
, validatorHashes !! 3 [1, 3 ..]
, validatorHashes !! 5
, validatorHashes !! 7
]
}
)
<$> template.authorityTokenParameters
} }
where where
template = mkValidFromLockedBundle nCosigners nEffects template = mkValidFromLockedBundle nCosigners nEffects
@ -1031,7 +1051,7 @@ mkNoGATMintedBundle ::
ParameterBundle ParameterBundle
mkNoGATMintedBundle nCosigners nEffects = mkNoGATMintedBundle nCosigners nEffects =
template template
{ authorityTokenParameters = Nothing { authorityTokenParameters = []
} }
where where
template = mkValidFromLockedBundle nCosigners nEffects template = mkValidFromLockedBundle nCosigners nEffects
@ -1059,16 +1079,19 @@ mkGATsWithWrongDatumBundle ::
ParameterBundle ParameterBundle
mkGATsWithWrongDatumBundle nCosigners nEffects = mkGATsWithWrongDatumBundle nCosigners nEffects =
template template
{ authorityTokenParameters = Just newAut { authorityTokenParameters = newAut
} }
where where
template = mkValidFromLockedBundle nCosigners nEffects template = mkValidFromLockedBundle nCosigners nEffects
aut = fromJust template.authorityTokenParameters
newAut = newAut =
AuthorityTokenParameters ( \aut ->
aut.mintGATsFor AuthorityTokenParameters
(Just (1 :: Integer)) aut.mintGATsFor
False (Just (1 :: Integer))
aut.carryRefScript
False
)
<$> template.authorityTokenParameters
mkBadGovernorOutputDatumBundle :: mkBadGovernorOutputDatumBundle ::
Word -> Word ->

View file

@ -26,6 +26,7 @@ import Agora.Governor (
) )
import Agora.Proposal ( import Agora.Proposal (
ProposalDatum (..), ProposalDatum (..),
ProposalEffectGroup,
ProposalId (ProposalId), ProposalId (ProposalId),
ProposalStatus (..), ProposalStatus (..),
ResultTag (ResultTag), ResultTag (ResultTag),
@ -60,12 +61,10 @@ import Plutarch.Context (
) )
import PlutusLedgerApi.V1.Value qualified as Value import PlutusLedgerApi.V1.Value qualified as Value
import PlutusLedgerApi.V2 ( import PlutusLedgerApi.V2 (
DatumHash,
POSIXTime (POSIXTime), POSIXTime (POSIXTime),
POSIXTimeRange, POSIXTimeRange,
PubKeyHash, PubKeyHash,
TxOutRef (TxOutRef), TxOutRef (TxOutRef),
ValidatorHash,
always, always,
) )
import PlutusTx.AssocMap qualified as AssocMap import PlutusTx.AssocMap qualified as AssocMap
@ -136,7 +135,7 @@ defLocks :: [ProposalLock]
defLocks = [Created (ProposalId 0)] defLocks = [Created (ProposalId 0)]
-- | The effect of the newly created proposal. -- | The effect of the newly created proposal.
defEffects :: AssocMap.Map ResultTag (AssocMap.Map ValidatorHash DatumHash) defEffects :: AssocMap.Map ResultTag ProposalEffectGroup
defEffects = defEffects =
AssocMap.fromList AssocMap.fromList
[ (ResultTag 0, AssocMap.empty) [ (ResultTag 0, AssocMap.empty)

View file

@ -28,6 +28,7 @@ module Sample.Proposal.UnlockStake (
import Agora.Governor (Governor (..)) import Agora.Governor (Governor (..))
import Agora.Proposal ( import Agora.Proposal (
ProposalDatum (..), ProposalDatum (..),
ProposalEffectGroup,
ProposalId (..), ProposalId (..),
ProposalRedeemer (Unlock), ProposalRedeemer (Unlock),
ProposalStatus (..), ProposalStatus (..),
@ -51,10 +52,8 @@ import Plutarch.Context (
) )
import PlutusLedgerApi.V1.Value qualified as Value import PlutusLedgerApi.V1.Value qualified as Value
import PlutusLedgerApi.V2 ( import PlutusLedgerApi.V2 (
DatumHash,
PubKeyHash, PubKeyHash,
TxOutRef (..), TxOutRef (..),
ValidatorHash,
) )
import PlutusTx.AssocMap qualified as AssocMap import PlutusTx.AssocMap qualified as AssocMap
import Sample.Proposal.Shared (stakeTxRef) import Sample.Proposal.Shared (stakeTxRef)
@ -85,7 +84,7 @@ votesTemplate =
-- | Create empty effects for every result tag given the votes. -- | Create empty effects for every result tag given the votes.
emptyEffectFor :: emptyEffectFor ::
ProposalVotes -> ProposalVotes ->
AssocMap.Map ResultTag (AssocMap.Map ValidatorHash DatumHash) AssocMap.Map ResultTag ProposalEffectGroup
emptyEffectFor (ProposalVotes vs) = emptyEffectFor (ProposalVotes vs) =
AssocMap.fromList $ AssocMap.fromList $
map (,AssocMap.empty) (AssocMap.keys vs) map (,AssocMap.empty) (AssocMap.keys vs)

View file

@ -15,6 +15,7 @@ module Test.Util (
sortValue, sortValue,
blake2b_224, blake2b_224,
pubKeyHashes, pubKeyHashes,
scriptHashes,
userCredentials, userCredentials,
scriptCredentials, scriptCredentials,
validatorHashes, validatorHashes,
@ -43,7 +44,6 @@ import Plutarch.Context (
) )
import Plutarch.Crypto (pblake2b_256) import Plutarch.Crypto (pblake2b_256)
import PlutusLedgerApi.V1.Interval qualified as PlutusTx import PlutusLedgerApi.V1.Interval qualified as PlutusTx
import PlutusLedgerApi.V1.Scripts (Datum (Datum), DatumHash (DatumHash))
import PlutusLedgerApi.V1.Value (Value (..)) import PlutusLedgerApi.V1.Value (Value (..))
import PlutusLedgerApi.V2 ( import PlutusLedgerApi.V2 (
Credential ( Credential (
@ -51,8 +51,11 @@ import PlutusLedgerApi.V2 (
ScriptCredential ScriptCredential
), ),
CurrencySymbol, CurrencySymbol,
Datum (Datum),
DatumHash (DatumHash),
PubKeyHash (..), PubKeyHash (..),
ScriptContext, ScriptContext,
ScriptHash (ScriptHash),
TxOutRef, TxOutRef,
ValidatorHash (ValidatorHash), ValidatorHash (ValidatorHash),
) )
@ -162,6 +165,10 @@ validatorHashes = ValidatorHash . PlutusTx.toBuiltin <$> blake2b_224Hashes
scriptCredentials :: [Credential] scriptCredentials :: [Credential]
scriptCredentials = ScriptCredential <$> validatorHashes scriptCredentials = ScriptCredential <$> validatorHashes
-- | An infinite list of *valid* script hashes.
scriptHashes :: [ScriptHash]
scriptHashes = ScriptHash . PlutusTx.toBuiltin <$> blake2b_224Hashes
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | Turn the given list in to groups which have the given length. -- | Turn the given list in to groups which have the given length.

View file

@ -28,6 +28,7 @@ import Agora.Governor (
) )
import Agora.Proposal ( import Agora.Proposal (
PProposalDatum (..), PProposalDatum (..),
PProposalEffectGroup,
ProposalStatus (Draft, Locked), ProposalStatus (Draft, Locked),
phasNeutralEffect, phasNeutralEffect,
pisEffectsVotesCompatible, pisEffectsVotesCompatible,
@ -36,7 +37,14 @@ import Agora.Proposal (
pwinner, pwinner,
) )
import Agora.Proposal.Time (createProposalStartingTime) import Agora.Proposal.Time (createProposalStartingTime)
import Agora.Scripts (AgoraScripts, authorityTokenSymbol, governorSTSymbol, proposalSTSymbol, proposalValidatoHash, stakeSTSymbol) import Agora.Scripts (
AgoraScripts,
authorityTokenSymbol,
governorSTSymbol,
proposalSTSymbol,
proposalValidatoHash,
stakeSTSymbol,
)
import Agora.Stake ( import Agora.Stake (
PProposalLock (..), PProposalLock (..),
PStakeDatum (..), PStakeDatum (..),
@ -45,17 +53,17 @@ import Agora.Stake (
import Agora.Utils ( import Agora.Utils (
pfindDatum, pfindDatum,
pfromDatumHash, pfromDatumHash,
pfstTuple,
pmustFindDatum, pmustFindDatum,
psndTuple,
validatorHashToAddress, validatorHashToAddress,
) )
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
PCurrencySymbol, PCurrencySymbol,
PMap,
PValidatorHash,
) )
import Plutarch.Api.V1.AssocMap qualified as AssocMap
import Plutarch.Api.V2 ( import Plutarch.Api.V2 (
PAddress, PAddress,
PDatumHash,
PMintingPolicy, PMintingPolicy,
PScriptPurpose (PMinting, PSpending), PScriptPurpose (PMinting, PSpending),
PTxOut, PTxOut,
@ -66,10 +74,9 @@ import Plutarch.Extra.Field (pletAllC)
import Plutarch.Extra.IsData (pmatchEnumFromData) import Plutarch.Extra.IsData (pmatchEnumFromData)
import Plutarch.Extra.List (pfirstJust) import Plutarch.Extra.List (pfirstJust)
import Plutarch.Extra.Map ( import Plutarch.Extra.Map (
plookup,
plookup', plookup',
) )
import Plutarch.Extra.Maybe (passertPJust, pfromJust, pnothing) import Plutarch.Extra.Maybe (passertPDJust, passertPJust, pfromJust, pmaybeData, pnothing)
import Plutarch.Extra.Record (mkRecordConstr, (.&), (.=)) import Plutarch.Extra.Record (mkRecordConstr, (.&), (.=))
import Plutarch.Extra.ScriptContext ( import Plutarch.Extra.ScriptContext (
pfindOutputsToAddress, pfindOutputsToAddress,
@ -489,35 +496,44 @@ governorValidator as =
pguardC "Output GATs is more than minted GATs" $ pguardC "Output GATs is more than minted GATs" $
plength # outputsWithGAT #== gatCount plength # outputsWithGAT #== gatCount
let gatOutputValidator' :: Term s (PMap _ PValidatorHash PDatumHash :--> PTxOut :--> PBool) let validateGATOutput' :: Term s (PProposalEffectGroup :--> PTxOut :--> PBool)
gatOutputValidator' = validateGATOutput' =
phoistAcyclic $ phoistAcyclic $
plam plam
( \effects output' -> unTermCont $ do ( \effects output -> unTermCont $ do
output <- pletFieldsC @'["address", "datum"] output' outputF <- pletFieldsC @'["address", "datum", "referenceScript"] output
let scriptHash = let receiverScriptHash =
passertPJust # "GAT receiver is not a script" passertPJust # "GAT receiver should be a script"
#$ pscriptHashFromAddress # output.address #$ pscriptHashFromAddress # outputF.address
datumHash = effect =
ptrace passertPJust # "Receiver should be in the effect group"
"Output to effect should have datum" #$ AssocMap.plookup # receiverScriptHash # effects
pfromDatumHash hasCorrectReferenceScript =
# output.datum pmaybeData
# pconstant True
expectedDatumHash = # plam
passertPJust # "Receiver is not in the effect list" ( ( passertPDJust
#$ plookup # scriptHash # effects # "Output UTXO should have a reference script"
# outputF.referenceScript
#==
)
. pfromData
)
# (psndTuple # effect)
hasCorrectDatum =
pfstTuple # effect #== pfromDatumHash # outputF.datum
pure $ pure $
foldr1 foldr1
(#&&) (#&&)
[ ptraceIfFalse "GAT must be tagged by the effect hash" $ authorityTokensValidIn # patSymbol # output' [ ptraceIfFalse "GAT valid" $ authorityTokensValidIn # patSymbol # output
, ptraceIfFalse "Unexpected datum" $ datumHash #== expectedDatumHash , ptraceIfFalse "Correct datum" hasCorrectDatum
, ptraceIfFalse "Reference script correct" hasCorrectReferenceScript
] ]
) )
gatOutputValidator = gatOutputValidator' # effectGroup validateGATOutput = validateGATOutput' # effectGroup
pguardC "GATs valid" $ pguardC "GATs valid" $
pfoldr pfoldr
@ -526,7 +542,7 @@ governorValidator as =
let value = pfield @"value" # txOut let value = pfield @"value" # txOut
atValue = psymbolValueOf # patSymbol # value atValue = psymbolValueOf # patSymbol # value
in pif (atValue #== 0) r $ in pif (atValue #== 0) r $
pif (atValue #== 1) (r #&& gatOutputValidator # txOut) $ pconstant False pif (atValue #== 1) (r #&& validateGATOutput # txOut) $ pconstant False
) )
# pconstant True # pconstant True
# pfromData txInfoF.outputs # pfromData txInfoF.outputs

View file

@ -7,7 +7,7 @@
module Agora.Plutarch.Orphans () where module Agora.Plutarch.Orphans () where
import Plutarch.Api.V1 (PDatumHash (..)) import Plutarch.Api.V2 (PDatumHash (..), PScriptHash (..))
import Plutarch.Builtin (PIsData (..)) import Plutarch.Builtin (PIsData (..))
import Plutarch.Extra.TermCont (ptryFromC) import Plutarch.Extra.TermCont (ptryFromC)
import Plutarch.TryFrom (PTryFrom (..)) import Plutarch.TryFrom (PTryFrom (..))
@ -37,3 +37,18 @@ instance PTryFrom PData (PAsData PUnit)
instance (PIsData a) => PIsData (PAsData a) where instance (PIsData a) => PIsData (PAsData a) where
pfromDataImpl = punsafeCoerce pfromDataImpl = punsafeCoerce
pdataImpl = pdataImpl . pfromData pdataImpl = pdataImpl . pfromData
-- | @since 1.0.0
instance PTryFrom PData (PAsData PScriptHash) where
type PTryFromExcess PData (PAsData PScriptHash) = Flip Term PScriptHash
ptryFrom' opq = runTermCont $ do
(pfromData -> unwrapped, _) <- ptryFromC @(PAsData PByteString) opq
tcont $ \f ->
pif
-- Blake2b_224 hash: 224 bits/28 bytes.
(plengthBS # unwrapped #== 28)
(f ())
(ptraceError "ptryFrom(PScriptHash): must be 32 bytes long")
pure (punsafeCoerce opq, pcon $ PScriptHash unwrapped)

View file

@ -11,6 +11,7 @@ module Agora.Proposal (
-- * Haskell-land -- * Haskell-land
-- Proposal (..), -- Proposal (..),
ProposalEffectGroup,
ProposalDatum (..), ProposalDatum (..),
ProposalRedeemer (..), ProposalRedeemer (..),
ProposalStatus (..), ProposalStatus (..),
@ -21,6 +22,7 @@ module Agora.Proposal (
emptyVotesFor, emptyVotesFor,
-- * Plutarch-land -- * Plutarch-land
PProposalEffectGroup,
PProposalDatum (..), PProposalDatum (..),
PProposalRedeemer (..), PProposalRedeemer (..),
PProposalStatus (..), PProposalStatus (..),
@ -41,7 +43,12 @@ module Agora.Proposal (
) where ) where
import Agora.Plutarch.Orphans () import Agora.Plutarch.Orphans ()
import Agora.Proposal.Time (PProposalStartingTime, PProposalTimingConfig, ProposalStartingTime, ProposalTimingConfig) import Agora.Proposal.Time (
PProposalStartingTime,
PProposalTimingConfig,
ProposalStartingTime,
ProposalTimingConfig,
)
import Agora.SafeMoney (GTTag) import Agora.SafeMoney (GTTag)
import Data.Tagged (Tagged) import Data.Tagged (Tagged)
import Generics.SOP qualified as SOP import Generics.SOP qualified as SOP
@ -50,7 +57,10 @@ import Plutarch.Api.V1.AssocMap qualified as PAssocMap
import Plutarch.Api.V2 ( import Plutarch.Api.V2 (
KeyGuarantees (Unsorted), KeyGuarantees (Unsorted),
PDatumHash, PDatumHash,
PMaybeData,
PPubKeyHash, PPubKeyHash,
PScriptHash,
PTuple,
) )
import Plutarch.DataRepr (DerivePConstantViaData (..), PDataFields) import Plutarch.DataRepr (DerivePConstantViaData (..), PDataFields)
import Plutarch.Extra.Comonad (pextract) import Plutarch.Extra.Comonad (pextract)
@ -75,7 +85,7 @@ import Plutarch.Lift (
) )
import Plutarch.SafeMoney (PDiscrete (..)) import Plutarch.SafeMoney (PDiscrete (..))
import Plutarch.Show (PShow (..)) import Plutarch.Show (PShow (..))
import PlutusLedgerApi.V1 (DatumHash, PubKeyHash, ValidatorHash) import PlutusLedgerApi.V2 (DatumHash, PubKeyHash, ScriptHash, ValidatorHash)
import PlutusTx qualified import PlutusTx qualified
import PlutusTx.AssocMap qualified as AssocMap import PlutusTx.AssocMap qualified as AssocMap
@ -272,6 +282,9 @@ newtype ProposalVotes = ProposalVotes
emptyVotesFor :: forall a. AssocMap.Map ResultTag a -> ProposalVotes emptyVotesFor :: forall a. AssocMap.Map ResultTag a -> ProposalVotes
emptyVotesFor = ProposalVotes . AssocMap.mapWithKey (const . const 0) emptyVotesFor = ProposalVotes . AssocMap.mapWithKey (const . const 0)
-- | @since 0.3.0
type ProposalEffectGroup = AssocMap.Map ValidatorHash (DatumHash, Maybe ScriptHash)
{- | Haskell-level datum for Proposal scripts. {- | Haskell-level datum for Proposal scripts.
@since 0.1.0 @since 0.1.0
@ -282,7 +295,7 @@ data ProposalDatum = ProposalDatum
-- TODO: could we encode this more efficiently? -- TODO: could we encode this more efficiently?
-- This is shaped this way for future proofing. -- This is shaped this way for future proofing.
-- See https://github.com/Liqwid-Labs/agora/issues/39 -- See https://github.com/Liqwid-Labs/agora/issues/39
, effects :: AssocMap.Map ResultTag (AssocMap.Map ValidatorHash DatumHash) , effects :: AssocMap.Map ResultTag ProposalEffectGroup
-- ^ Effect lookup table. First by result, then by effect hash. -- ^ Effect lookup table. First by result, then by effect hash.
, status :: ProposalStatus , status :: ProposalStatus
-- ^ The status the proposal is in. -- ^ The status the proposal is in.
@ -583,6 +596,15 @@ deriving via
instance instance
(PConstantDecl ProposalVotes) (PConstantDecl ProposalVotes)
type PProposalEffectGroup =
PMap
'Unsorted
PValidatorHash
( PTuple
PDatumHash
(PMaybeData (PAsData PScriptHash))
)
{- | Plutarch-level version of 'ProposalDatum'. {- | Plutarch-level version of 'ProposalDatum'.
@since 0.1.0 @since 0.1.0
@ -593,7 +615,7 @@ newtype PProposalDatum (s :: S) = PProposalDatum
s s
( PDataRecord ( PDataRecord
'[ "proposalId" ':= PProposalId '[ "proposalId" ':= PProposalId
, "effects" ':= PMap 'Unsorted PResultTag (PMap 'Unsorted PValidatorHash PDatumHash) , "effects" ':= PMap 'Unsorted PResultTag PProposalEffectGroup
, "status" ':= PProposalStatus , "status" ':= PProposalStatus
, "cosigners" ':= PBuiltinList (PAsData PPubKeyHash) , "cosigners" ':= PBuiltinList (PAsData PPubKeyHash)
, "thresholds" ':= PProposalThresholds , "thresholds" ':= PProposalThresholds
@ -678,7 +700,7 @@ phasNeutralEffect ::
forall (s :: S). forall (s :: S).
Term Term
s s
( PMap 'Unsorted PResultTag (PMap 'Unsorted PValidatorHash PDatumHash) ( PMap 'Unsorted PResultTag PProposalEffectGroup
:--> PBool :--> PBool
) )
phasNeutralEffect = phoistAcyclic $ PAssocMap.pany # PAssocMap.pnull phasNeutralEffect = phoistAcyclic $ PAssocMap.pany # PAssocMap.pnull
@ -691,7 +713,7 @@ pisEffectsVotesCompatible ::
forall (s :: S). forall (s :: S).
Term Term
s s
( PMap 'Unsorted PResultTag (PMap 'Unsorted PValidatorHash PDatumHash) ( PMap 'Unsorted PResultTag PProposalEffectGroup
:--> PProposalVotes :--> PProposalVotes
:--> PBool :--> PBool
) )
@ -811,7 +833,7 @@ phighestVotes = phoistAcyclic $
pneutralOption :: pneutralOption ::
Term Term
s s
( PMap 'Unsorted PResultTag (PMap 'Unsorted PValidatorHash PDatumHash) ( PMap 'Unsorted PResultTag PProposalEffectGroup
:--> PResultTag :--> PResultTag
) )
pneutralOption = phoistAcyclic $ pneutralOption = phoistAcyclic $

View file

@ -44,8 +44,9 @@ import Plutarch.DataRepr (
PDataFields, PDataFields,
) )
import Plutarch.Extra.Applicative (PApply (pliftA2)) import Plutarch.Extra.Applicative (PApply (pliftA2))
import Plutarch.Extra.Bind ((#>>=))
import Plutarch.Extra.Field (pletAll, pletAllC) import Plutarch.Extra.Field (pletAll, pletAllC)
import Plutarch.Extra.Maybe (pjust, pmaybe, pnothing) import Plutarch.Extra.Maybe (pjust, pnothing)
import Plutarch.Extra.TermCont (pmatchC) import Plutarch.Extra.TermCont (pmatchC)
import Plutarch.Lift ( import Plutarch.Lift (
DerivePConstantViaNewtype (..), DerivePConstantViaNewtype (..),
@ -357,8 +358,7 @@ createProposalStartingTime = phoistAcyclic $
"createProposalStartingTime: given time range should be tight enough" "createProposalStartingTime: given time range should be tight enough"
pnothing pnothing
) )
in -- TODO: PMonad when? in ct #>>= f
pmaybe # pnothing # f # ct
{- | Get the current proposal time, from the 'PlutusLedgerApi.V1.txInfoValidPeriod' field. {- | Get the current proposal time, from the 'PlutusLedgerApi.V1.txInfoValidPeriod' field.

View file

@ -24,6 +24,8 @@ module Agora.Utils (
pfromDatumHash, pfromDatumHash,
pfromInlineDatum, pfromInlineDatum,
ptryFindDatum, ptryFindDatum,
pfstTuple,
psndTuple,
) where ) where
import Plutarch.Api.V1.AssocMap (KeyGuarantees (Unsorted), PMap) import Plutarch.Api.V1.AssocMap (KeyGuarantees (Unsorted), PMap)
@ -32,6 +34,7 @@ import Plutarch.Api.V2 (
PDatum, PDatum,
PDatumHash, PDatumHash,
POutputDatum (..), POutputDatum (..),
PTuple,
) )
import Plutarch.Extra.Functor (pfmap) import Plutarch.Extra.Functor (pfmap)
import Plutarch.Extra.Maybe (passertPJust, pjust, pnothing) import Plutarch.Extra.Maybe (passertPJust, pjust, pnothing)
@ -235,3 +238,17 @@ infixr 8 #.**
Term s c -> Term s c ->
Term s e Term s e
(#.**) f g x y z = f #$ g # x # y # z (#.**) f g x y z = f #$ g # x # y # z
{- | Extract the first component of a 'PTuple'.
@since 1.0.0
-}
pfstTuple :: forall a b s. (PIsData a) => Term s (PTuple a b :--> a)
pfstTuple = phoistAcyclic $ plam $ pfromData . (pfield @"_0" #)
{- | Extract the second component of a 'PTuple'.
@since 1.0.0
-}
psndTuple :: forall b a s. (PIsData b) => Term s (PTuple a b :--> b)
psndTuple = phoistAcyclic $ plam $ pfromData . (pfield @"_1" #)

1037
bench.csv

File diff suppressed because it is too large Load diff

8
flake.lock generated
View file

@ -12940,16 +12940,16 @@
"plutarch": "plutarch_7" "plutarch": "plutarch_7"
}, },
"locked": { "locked": {
"lastModified": 1660151677, "lastModified": 1660709756,
"narHash": "sha256-sUdXtVdCpm7pgmw0f+b0FyF4Bf0VX+1kTY5U8O3H4uk=", "narHash": "sha256-L9S9UYtpeTVGFmKNj8c/H3i17Jx8asCiW43xblN5nBo=",
"owner": "Liqwid-Labs", "owner": "Liqwid-Labs",
"repo": "plutarch-context-builder", "repo": "plutarch-context-builder",
"rev": "7033ca3c18968cfd206afbbd40861a0a778b414c", "rev": "cc91cd134ce01e280df3b0ce09a25ede54dbf9b6",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "Liqwid-Labs", "owner": "Liqwid-Labs",
"ref": "staging", "ref": "main",
"repo": "plutarch-context-builder", "repo": "plutarch-context-builder",
"type": "github" "type": "github"
} }

View file

@ -32,7 +32,7 @@
plutarch-quickcheck.url = plutarch-quickcheck.url =
"github:liqwid-labs/plutarch-quickcheck?ref=staging"; "github:liqwid-labs/plutarch-quickcheck?ref=staging";
plutarch-context-builder.url = plutarch-context-builder.url =
"github:Liqwid-Labs/plutarch-context-builder?ref=staging"; "github:Liqwid-Labs/plutarch-context-builder?ref=main";
plutarch-script-export.url = plutarch-script-export.url =
"github:Liqwid-Labs/plutarch-script-export?ref=main"; "github:Liqwid-Labs/plutarch-script-export?ref=main";