agora/agora-specs/Sample/Proposal/Cosign.hs
Seungheon Oh d2018afd4d Use liqwid-script-export
commit ec70bfd539fe2e27fd48f5f76395400287ac72d7
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date:   Tue Oct 18 18:58:59 2022 -0500

    use LSE

commit 25fff9b3ad1f2dde4cd7cf36977530b06a87d23c
Merge: 01cd3aa 1821dd6
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date:   Tue Oct 18 18:17:45 2022 -0500

    Merge branch 'staging' into seungheonoh/ply

commit 01cd3aa7a235e6fe6658246ca1026fa26dc71a83
Author: Hongrui Fang <chfanghr@gmail.com>
Date:   Tue Oct 11 12:02:03 2022 +0800

    update benchmark

commit a8513244892ce33cfdc9edf8cd501c4985ae8008
Author: Hongrui Fang <chfanghr@gmail.com>
Date:   Tue Oct 11 11:59:22 2022 +0800

    fix tests

commit 20ca40823485c2e2f78253643cf4453ac7b7ddd5
Author: Hongrui Fang <chfanghr@gmail.com>
Date:   Tue Oct 11 11:57:37 2022 +0800

    better import

commit a19fe49424210891bd03db71e4083fc1e0edfd98
Author: Hongrui Fang <chfanghr@gmail.com>
Date:   Tue Oct 11 11:08:20 2022 +0800

    update flake inputs

commit c93b21f1f9441e5c6f54525bf7c6a54757ec36cc
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date:   Mon Oct 10 12:54:12 2022 -0500

    tried to make tests pass

commit 1046ae1237299a33c58b48661bdb6d325a22147e
Merge: 2bf4e36 363bd83
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date:   Mon Oct 10 12:18:48 2022 -0500

    Merge branch 'staging' into seungheonoh/ply

commit 2bf4e3627c1b229f58078695082da85c80efd560
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date:   Mon Oct 10 10:48:36 2022 -0500

    remove junkpile

commit a1dbc9ad9e531fe0d0a0480c4aef9cf9ffa90f1d
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date:   Mon Oct 10 10:47:25 2022 -0500

    versions

commit 4542a06ac733858297d3a48c53368fad19dedc43
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date:   Thu Oct 6 22:57:48 2022 -0500

    script exporting interface

commit 6bd8c1a1d57e4bf9dc25c3068a9c8eae6bf6a19d
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date:   Thu Oct 6 22:58:41 2022 -0500

    fixed tests

commit d3ce2cf95633d336f3e621833677bd5bf10ee2c8
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date:   Sun Oct 2 00:55:18 2022 -0500

    fixed tests

commit 1ae64c9f692652b77b0506013853b2ba44267c65
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date:   Sat Oct 1 13:28:20 2022 -0500

    linker

commit db88cb75c7b74843141ad8ab4e6522b66d0dcfbc
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date:   Sat Oct 1 01:03:50 2022 -0500

    exporting scripts

commit 6389fce28e885a8a7f8669629c266f59c0edb51f
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date:   Sat Oct 1 00:51:49 2022 -0500

    made scripts parameterized on the script level

commit aea1e518a8890550bdebd0e5251da11d915c53a9
Author: Seungheon Oh <seungheon.ooh@gmail.com>
Date:   Wed Sep 28 19:53:29 2022 -0500

    Use `TypedScriptEnvelope` for `Agora.Bootstrap`
2022-10-18 19:02:10 -05:00

420 lines
10 KiB
Haskell

{- |
Module : Sample.Proposal.Cosign
Maintainer : connor@mlabs.city
Description: Generate sample data for testing the functionalities of cosigning proposals
Sample and utilities for testing the functionalities of cosigning proposals.
-}
module Sample.Proposal.Cosign (
StakedAmount (..),
StakeOwner (..),
StakeParameters (..),
SignedBy (..),
TransactionParameters (..),
ProposalParameters (..),
ParameterBundle (..),
Validity (..),
cosign,
mkTestTree,
totallyValid,
insufficientStakedAmount,
duplicateCosigners,
locksNotUpdated,
cosignersNotUpdated,
cosignAfterDraft,
) where
import Agora.Governor (Governor (..))
import Agora.Proposal (
ProposalDatum (..),
ProposalId (ProposalId),
ProposalRedeemer (Cosign),
ProposalStatus (..),
ProposalThresholds (..),
ResultTag (ResultTag),
emptyVotesFor,
)
import Agora.Proposal.Time (
ProposalStartingTime (ProposalStartingTime),
ProposalTimingConfig (draftTime),
)
import Agora.SafeMoney (GTTag)
import Agora.Stake (
ProposalLock (Cosigned, Created),
StakeDatum (..),
StakeRedeemer (PermitVote),
)
import Data.Coerce (coerce)
import Data.Default (def)
import Data.List (sort)
import Data.Map.Strict qualified as StrictMap
import Data.Tagged (untag)
import Plutarch.Context (
input,
normalizeValue,
output,
script,
signedWith,
timeRange,
txId,
withDatum,
withInlineDatum,
withRedeemer,
withRef,
withValue,
)
import Plutarch.SafeMoney (Discrete (Discrete))
import PlutusLedgerApi.V1.Value qualified as Value
import PlutusLedgerApi.V2 (
Credential (PubKeyCredential),
POSIXTime (POSIXTime),
PubKeyHash,
TxOutRef (TxOutRef),
)
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef)
import Sample.Shared (
fromDiscrete,
governor,
minAda,
proposalPolicySymbol,
proposalValidator,
proposalValidatorHash,
stakeAssetClass,
stakeValidator,
stakeValidatorHash,
)
import Test.Specification (
SpecificationTree,
group,
testValidator,
)
import Test.Util (
CombinableBuilder,
closedBoundedInterval,
mkSpending,
pubKeyHashes,
)
data StakedAmount = Sufficient | Insufficient
data StakeOwner = Creator | Other
data StakeParameters = StakeParameters
{ gtAmount :: StakedAmount
, stakeOwner :: StakeOwner
, dontUpdateLocks :: Bool
}
data SignedBy = Owner | Delegatee | Unknown
newtype TransactionParameters = TransactionParameters
{ signedBy :: SignedBy
}
data ProposalParameters = ProposalParameters
{ proposalStatus :: ProposalStatus
, dontUpdateCosigners :: Bool
}
-- | Parameters for cosigning a proposal.
data ParameterBundle = ParameterBundle
{ stakeParameters :: StakeParameters
, proposalParameters :: ProposalParameters
, transactionParameters :: TransactionParameters
}
data Validity = Validity
{ forProposalValidator :: Bool
, forStakeValidator :: Bool
}
--------------------------------------------------------------------------------
mkStakeAmount :: StakedAmount -> Discrete GTTag
mkStakeAmount Sufficient = Discrete $ (def @ProposalThresholds).cosign
mkStakeAmount Insufficient = mkStakeAmount Sufficient - 1
mkStakeOwner :: StakeOwner -> PubKeyHash
mkStakeOwner Creator = creator
mkStakeOwner Other = pubKeyHashes !! 2
mkSigner :: StakeOwner -> SignedBy -> PubKeyHash
mkSigner so Owner = mkStakeOwner so
mkSigner _ Delegatee = delegatee
mkSigner _ Unknown = pubKeyHashes !! 4
creator :: PubKeyHash
creator = pubKeyHashes !! 1
delegatee :: PubKeyHash
delegatee = pubKeyHashes !! 3
--------------------------------------------------------------------------------
defProposalId :: ProposalId
defProposalId = ProposalId 0
mkProposalInputDatum :: ParameterBundle -> ProposalDatum
mkProposalInputDatum ps =
let effects =
StrictMap.fromList
[ (ResultTag 0, StrictMap.empty)
, (ResultTag 1, StrictMap.empty)
]
in ProposalDatum
{ proposalId = ProposalId 0
, effects = effects
, status = ps.proposalParameters.proposalStatus
, cosigners = [PubKeyCredential creator]
, thresholds = def
, votes = emptyVotesFor effects
, timingConfig = def
, startingTime = ProposalStartingTime 0
}
mkProposalOutputDatum :: ParameterBundle -> ProposalDatum
mkProposalOutputDatum ps =
let inputDatum = mkProposalInputDatum ps
stakeOwner =
PubKeyCredential $
mkStakeOwner ps.stakeParameters.stakeOwner
newCosigners =
if ps.proposalParameters.dontUpdateCosigners
then inputDatum.cosigners
else sort $ stakeOwner : inputDatum.cosigners
in inputDatum {cosigners = newCosigners}
proposalRedeemer :: ProposalRedeemer
proposalRedeemer = Cosign
proposalRef :: TxOutRef
proposalRef = TxOutRef proposalTxRef 1
--------------------------------------------------------------------------------
mkStakeInputDatum :: ParameterBundle -> StakeDatum
mkStakeInputDatum ps =
let sps = ps.stakeParameters
amount = mkStakeAmount sps.gtAmount
owner = mkStakeOwner sps.stakeOwner
locks = case sps.stakeOwner of
Creator -> [Created defProposalId]
_ -> []
in StakeDatum
{ stakedAmount = amount
, owner = PubKeyCredential owner
, delegatedTo = Just $ PubKeyCredential delegatee
, lockedBy = locks
}
mkStakeOuputDatum :: ParameterBundle -> StakeDatum
mkStakeOuputDatum ps =
let sps = ps.stakeParameters
inpDatum = mkStakeInputDatum ps
locks =
if sps.dontUpdateLocks
then inpDatum.lockedBy
else Cosigned defProposalId : inpDatum.lockedBy
in inpDatum {lockedBy = locks}
stakeRedeemer :: StakeRedeemer
stakeRedeemer = PermitVote
stakeRef :: TxOutRef
stakeRef = TxOutRef stakeTxRef 0
--------------------------------------------------------------------------------
-- | Create a 'TxInfo' that tries to cosign a proposal with new cosigners.
cosign :: forall b. CombinableBuilder b => ParameterBundle -> b
cosign ps = builder
where
pst = Value.singleton proposalPolicySymbol "" 1
sst = Value.assetClassValue stakeAssetClass 1
----------------------------------------------------------------------------
stakeInputDatum = mkStakeInputDatum ps
stakeOutputDatum = mkStakeOuputDatum ps
stakeValue =
normalizeValue $
minAda
<> Value.assetClassValue
(untag governor.gtClassRef)
( fromDiscrete $
mkStakeAmount ps.stakeParameters.gtAmount
)
<> sst
stakeBuilder =
mconcat
[ input $
mconcat
[ script stakeValidatorHash
, withValue stakeValue
, withInlineDatum stakeInputDatum
, withRef stakeRef
, withRedeemer stakeRedeemer
]
, output $
mconcat
[ script stakeValidatorHash
, withValue stakeValue
, withInlineDatum stakeOutputDatum
]
]
----------------------------------------------------------------------------
proposalInputDatum = mkProposalInputDatum ps
proposalOutputDatum = mkProposalOutputDatum ps
proposalValue =
normalizeValue $
pst <> minAda
proposalBuilder =
mconcat
[ input $
mconcat
[ script proposalValidatorHash
, withValue proposalValue
, withDatum proposalInputDatum
, withRef proposalRef
, withRedeemer proposalRedeemer
]
, output $
mconcat
[ script proposalValidatorHash
, withValue proposalValue
, withDatum proposalOutputDatum
]
]
----------------------------------------------------------------------------
validTimeRange =
closedBoundedInterval
(coerce proposalInputDatum.startingTime + 1)
( coerce proposalInputDatum.startingTime
+ proposalInputDatum.timingConfig.draftTime - 1
)
sig =
mkSigner
ps.stakeParameters.stakeOwner
ps.transactionParameters.signedBy
----------------------------------------------------------------------------
builder =
mconcat
[ txId "05c67819fc3381a2052b929ab439244b7b5fe3b3bd07f2134055bbbb21bd9e52"
, timeRange validTimeRange
, proposalBuilder
, stakeBuilder
, signedWith sig
]
--------------------------------------------------------------------------------
mkTestTree ::
String ->
ParameterBundle ->
Validity ->
SpecificationTree
mkTestTree name ps val =
group name [proposal, stake]
where
spend = mkSpending cosign ps
proposal =
testValidator
val.forProposalValidator
"proposal"
proposalValidator
(mkProposalInputDatum ps)
proposalRedeemer
(spend proposalRef)
stake =
testValidator
val.forStakeValidator
"stake"
stakeValidator
(mkStakeInputDatum ps)
stakeRedeemer
(spend stakeRef)
--------------------------------------------------------------------------------
totallyValid :: ParameterBundle
totallyValid =
ParameterBundle
{ stakeParameters =
StakeParameters
{ gtAmount = Sufficient
, stakeOwner = Other
, dontUpdateLocks = False
}
, proposalParameters =
ProposalParameters
{ proposalStatus = Draft
, dontUpdateCosigners = False
}
, transactionParameters =
TransactionParameters
{ signedBy =
Owner
}
}
insufficientStakedAmount :: ParameterBundle
insufficientStakedAmount =
totallyValid
{ stakeParameters =
totallyValid.stakeParameters
{ gtAmount = Insufficient
}
}
locksNotUpdated :: ParameterBundle
locksNotUpdated =
totallyValid
{ stakeParameters =
totallyValid.stakeParameters
{ dontUpdateLocks = True
}
}
duplicateCosigners :: ParameterBundle
duplicateCosigners =
totallyValid
{ stakeParameters =
totallyValid.stakeParameters
{ stakeOwner = Creator
}
}
cosignersNotUpdated :: ParameterBundle
cosignersNotUpdated =
totallyValid
{ proposalParameters =
totallyValid.proposalParameters
{ dontUpdateCosigners = True
}
}
cosignAfterDraft :: [ParameterBundle]
cosignAfterDraft =
map
( \s ->
totallyValid
{ proposalParameters =
totallyValid.proposalParameters
{ proposalStatus = s
}
}
)
[VotingReady, Locked, Finished]