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`
542 lines
14 KiB
Haskell
542 lines
14 KiB
Haskell
{- |
|
|
Module : Sample.Proposal.UnlockStake
|
|
Maintainer : connor@mlabs.city
|
|
Description: Generate sample data for testing the functionalities of unlocking stake and retracting votes
|
|
|
|
Sample and utilities for testing the functionalities of unlocking stake and retracting votes
|
|
-}
|
|
module Sample.Proposal.Unlock (
|
|
ParameterBundle (..),
|
|
StakeRole (..),
|
|
TimeRange (..),
|
|
SignedBy (..),
|
|
TransactionParameters (..),
|
|
ProposalParameters (..),
|
|
StakeParameters (..),
|
|
Validity (..),
|
|
unlock,
|
|
mkTestTree,
|
|
mkValidVoterRetractVotes,
|
|
mkValidDelegateeRetractVotes,
|
|
mkValidVoterCreatorRetractVotes,
|
|
mkValidCreatorRemoveLock,
|
|
mkValidVoterRemoveLockAfterVoting,
|
|
mkRetractVotesWhileNotVoting,
|
|
mkUnockIrrelevantStakes,
|
|
mkRemoveCreatorLockBeforeFinished,
|
|
mkCreatorRetractVotes,
|
|
mkChangeOutputStakeValue,
|
|
) where
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
import Agora.Governor (Governor (..))
|
|
import Agora.Proposal (
|
|
ProposalDatum (..),
|
|
ProposalEffectGroup,
|
|
ProposalId (..),
|
|
ProposalRedeemer (Unlock),
|
|
ProposalStatus (..),
|
|
ProposalVotes (..),
|
|
ResultTag (..),
|
|
)
|
|
import Agora.Proposal.Time (ProposalStartingTime (ProposalStartingTime), ProposalTimingConfig (..))
|
|
import Agora.Stake (
|
|
ProposalLock (..),
|
|
StakeDatum (..),
|
|
StakeRedeemer (RetractVotes),
|
|
)
|
|
import Data.Default.Class (Default (def))
|
|
import Data.Map.Strict qualified as StrictMap
|
|
import Data.Tagged (Tagged (Tagged), untag)
|
|
import Plutarch.Context (
|
|
input,
|
|
normalizeValue,
|
|
output,
|
|
script,
|
|
signedWith,
|
|
timeRange,
|
|
txId,
|
|
withDatum,
|
|
withRedeemer,
|
|
withRef,
|
|
withValue,
|
|
)
|
|
import Plutarch.SafeMoney (Discrete (Discrete))
|
|
import PlutusLedgerApi.V1.Value qualified as Value
|
|
import PlutusLedgerApi.V2 (
|
|
Credential (PubKeyCredential),
|
|
PubKeyHash,
|
|
TxOutRef (..),
|
|
)
|
|
import Sample.Proposal.Shared (stakeTxRef)
|
|
import Sample.Shared (
|
|
governor,
|
|
minAda,
|
|
proposalPolicySymbol,
|
|
proposalValidator,
|
|
proposalValidatorHash,
|
|
stakeAssetClass,
|
|
stakeValidator,
|
|
stakeValidatorHash,
|
|
)
|
|
import Test.Specification (SpecificationTree, group, testValidator)
|
|
import Test.Util (CombinableBuilder, closedBoundedInterval, mkSpending, pubKeyHashes)
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
votesTemplate :: ProposalVotes
|
|
votesTemplate =
|
|
ProposalVotes $
|
|
StrictMap.fromList
|
|
[ (ResultTag 0, 0)
|
|
, (ResultTag 1, 0)
|
|
]
|
|
|
|
-- | Create empty effects for every result tag given the votes.
|
|
emptyEffectFor ::
|
|
ProposalVotes ->
|
|
StrictMap.Map ResultTag ProposalEffectGroup
|
|
emptyEffectFor (ProposalVotes vs) =
|
|
StrictMap.fromList $
|
|
map (,StrictMap.empty) (StrictMap.keys vs)
|
|
|
|
-- | The default vote option that will be used by functions in this module.
|
|
defVoteFor :: ResultTag
|
|
defVoteFor = ResultTag 0
|
|
|
|
-- | The default number of GTs the stake will have.
|
|
defStakedGTs :: Integer
|
|
defStakedGTs = 100000
|
|
|
|
alteredStakedGTs :: Integer
|
|
alteredStakedGTs = 100
|
|
|
|
-- | Default owner of the stakes.
|
|
defOwner :: PubKeyHash
|
|
defOwner = pubKeyHashes !! 1
|
|
|
|
defDelegatee :: PubKeyHash
|
|
defDelegatee = pubKeyHashes !! 2
|
|
|
|
defUnknown :: PubKeyHash
|
|
defUnknown = pubKeyHashes !! 3
|
|
|
|
defProposalId :: ProposalId
|
|
defProposalId = ProposalId 0
|
|
|
|
defStartingTime :: ProposalStartingTime
|
|
defStartingTime = ProposalStartingTime 0
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
data ParameterBundle = ParameterBundle
|
|
{ proposalParameters :: ProposalParameters
|
|
, stakeParameters :: StakeParameters
|
|
, transactionParameters :: TransactionParameters
|
|
}
|
|
|
|
data SignedBy = Owner | Delegatee | Unknown
|
|
|
|
data TimeRange = WhileVoting | AfterVoting
|
|
|
|
data TransactionParameters = TransactionParameters
|
|
{ signedBy :: SignedBy
|
|
, timeRange :: TimeRange
|
|
}
|
|
|
|
data ProposalParameters = ProposalParameters
|
|
{ proposalStatus :: ProposalStatus
|
|
, retractVotes :: Bool
|
|
}
|
|
|
|
-- | How a stake has been used on a particular proposal.
|
|
data StakeRole
|
|
= -- | The stake was spent to vote for a paraticular option.
|
|
Voter
|
|
| -- | The stake was used to create the proposal.
|
|
Creator
|
|
| -- | The stake was used to both create and vote for the proposal.
|
|
Both
|
|
| -- | The stake has nothing to do with the proposal.
|
|
Irrelevant
|
|
deriving stock (Bounded, Enum, Show)
|
|
|
|
data StakeParameters = StakeParameters
|
|
{ numStakes :: Integer
|
|
, stakeRole :: StakeRole
|
|
, removeVoterLock :: Bool
|
|
, removeCreatorLock :: Bool
|
|
, alterOutputValue :: Bool
|
|
}
|
|
|
|
data Validity = Validity
|
|
{ forProposalValidator :: Bool
|
|
, forStakeValidator :: Bool
|
|
}
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
mkStakeRef :: Integer -> TxOutRef
|
|
mkStakeRef = TxOutRef stakeTxRef
|
|
|
|
stakeRedeemer :: StakeRedeemer
|
|
stakeRedeemer = RetractVotes
|
|
|
|
mkStakeInputDatum :: StakeParameters -> StakeDatum
|
|
mkStakeInputDatum ps =
|
|
StakeDatum
|
|
{ stakedAmount = Discrete $ Tagged defStakedGTs
|
|
, owner = PubKeyCredential defOwner
|
|
, delegatedTo = Just $ PubKeyCredential defDelegatee
|
|
, lockedBy = stakeLocks
|
|
}
|
|
where
|
|
stakeLocks = mkStakeLocks' ps.stakeRole
|
|
|
|
mkStakeLocks' Voter = [Voted defProposalId defVoteFor]
|
|
mkStakeLocks' Creator = [Created defProposalId]
|
|
mkStakeLocks' Both = mkStakeLocks' Voter <> mkStakeLocks' Creator
|
|
mkStakeLocks' Irrelevant =
|
|
let ProposalId pid = defProposalId
|
|
ResultTag vid = defVoteFor
|
|
in [ Voted (ProposalId $ pid + 1) (ResultTag $ vid + 1)
|
|
, Created (ProposalId $ pid + 1)
|
|
]
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
proposalRef :: TxOutRef
|
|
proposalRef = TxOutRef stakeTxRef 0
|
|
|
|
proposalRedeemer :: ProposalRedeemer
|
|
proposalRedeemer = Unlock
|
|
|
|
mkProposalInputDatum ::
|
|
StakeParameters ->
|
|
ProposalParameters ->
|
|
ProposalDatum
|
|
mkProposalInputDatum sps pps =
|
|
ProposalDatum
|
|
{ proposalId = defProposalId
|
|
, effects = emptyEffectFor votesTemplate
|
|
, status = pps.proposalStatus
|
|
, cosigners = [PubKeyCredential $ head pubKeyHashes]
|
|
, thresholds = def
|
|
, votes = updatVotes votesTemplate
|
|
, timingConfig = def
|
|
, startingTime = defStartingTime
|
|
}
|
|
where
|
|
updatVotes (ProposalVotes vt) =
|
|
ProposalVotes $
|
|
StrictMap.adjust
|
|
(+ sps.numStakes * defStakedGTs)
|
|
defVoteFor
|
|
vt
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
unlock :: forall b. CombinableBuilder b => ParameterBundle -> b
|
|
unlock ps = builder
|
|
where
|
|
pst = Value.singleton proposalPolicySymbol "" 1
|
|
|
|
proposalInputDatum =
|
|
mkProposalInputDatum
|
|
ps.stakeParameters
|
|
ps.proposalParameters
|
|
|
|
proposalOutputDatum =
|
|
if ps.proposalParameters.retractVotes
|
|
then proposalInputDatum {votes = votesTemplate}
|
|
else proposalInputDatum
|
|
|
|
proposalValue = normalizeValue $ pst <> minAda
|
|
|
|
proposalBuilder :: b
|
|
proposalBuilder =
|
|
mconcat
|
|
[ input $
|
|
mconcat
|
|
[ script proposalValidatorHash
|
|
, withValue proposalValue
|
|
, withDatum proposalInputDatum
|
|
, withRef proposalRef
|
|
, withRedeemer proposalRedeemer
|
|
]
|
|
, output $
|
|
mconcat
|
|
[ script proposalValidatorHash
|
|
, withValue proposalValue
|
|
, withDatum proposalOutputDatum
|
|
]
|
|
]
|
|
|
|
---
|
|
|
|
sst = Value.assetClassValue stakeAssetClass 1
|
|
|
|
stakeInputDatum = mkStakeInputDatum ps.stakeParameters
|
|
|
|
removeLocks v c =
|
|
filter $
|
|
not
|
|
. ( \case
|
|
Created pid -> c && pid == defProposalId
|
|
Cosigned pid -> c && pid == defProposalId
|
|
Voted pid _ -> v && pid == defProposalId
|
|
)
|
|
|
|
stakeOutputDatum =
|
|
stakeInputDatum
|
|
{ lockedBy =
|
|
removeLocks
|
|
ps.stakeParameters.removeVoterLock
|
|
ps.stakeParameters.removeCreatorLock
|
|
stakeInputDatum.lockedBy
|
|
}
|
|
|
|
mkStakeValue gt =
|
|
normalizeValue $
|
|
mconcat
|
|
[ minAda
|
|
, sst
|
|
, Value.assetClassValue
|
|
(untag governor.gtClassRef)
|
|
gt
|
|
]
|
|
|
|
stakeInputValue = mkStakeValue defStakedGTs
|
|
|
|
stakeOutputValue =
|
|
mkStakeValue $
|
|
if ps.stakeParameters.alterOutputValue
|
|
then alteredStakedGTs
|
|
else defStakedGTs
|
|
|
|
stakeBuilder :: b
|
|
stakeBuilder =
|
|
foldMap
|
|
( \i ->
|
|
mconcat
|
|
[ input $
|
|
mconcat
|
|
[ script stakeValidatorHash
|
|
, withValue stakeInputValue
|
|
, withDatum stakeInputDatum
|
|
, withRef $ mkStakeRef i
|
|
]
|
|
, output $
|
|
mconcat
|
|
[ script stakeValidatorHash
|
|
, withValue stakeOutputValue
|
|
, withDatum stakeOutputDatum
|
|
]
|
|
]
|
|
)
|
|
[1 .. ps.stakeParameters.numStakes]
|
|
|
|
---
|
|
|
|
time = case ps.transactionParameters.timeRange of
|
|
WhileVoting ->
|
|
closedBoundedInterval
|
|
((def :: ProposalTimingConfig).draftTime + 1)
|
|
((def :: ProposalTimingConfig).votingTime - 1)
|
|
AfterVoting ->
|
|
closedBoundedInterval
|
|
((def :: ProposalTimingConfig).votingTime + 1)
|
|
((def :: ProposalTimingConfig).lockingTime - 1)
|
|
|
|
sig = case ps.transactionParameters.signedBy of
|
|
Unknown -> defUnknown
|
|
Owner -> defOwner
|
|
Delegatee -> defDelegatee
|
|
|
|
---
|
|
|
|
builder =
|
|
mconcat
|
|
[ txId "388bc0b897b3dadcd479da4c88291de4113a50b72ddbed001faf7fc03f11bc52"
|
|
, proposalBuilder
|
|
, stakeBuilder
|
|
, signedWith sig
|
|
, timeRange time
|
|
]
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
{- | Create a test tree that runs both the stake validator and the proposal
|
|
validator.
|
|
-}
|
|
mkTestTree :: String -> ParameterBundle -> Validity -> SpecificationTree
|
|
mkTestTree name ps val = group name [stake, proposal]
|
|
where
|
|
spend = mkSpending unlock ps
|
|
|
|
stake =
|
|
testValidator
|
|
val.forStakeValidator
|
|
"stake"
|
|
stakeValidator
|
|
(mkStakeInputDatum ps.stakeParameters)
|
|
stakeRedeemer
|
|
(spend $ mkStakeRef 1)
|
|
|
|
proposal =
|
|
testValidator
|
|
val.forProposalValidator
|
|
"proposal"
|
|
proposalValidator
|
|
(mkProposalInputDatum ps.stakeParameters ps.proposalParameters)
|
|
proposalRedeemer
|
|
(spend proposalRef)
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
mkValidVoterRetractVotes :: Integer -> ParameterBundle
|
|
mkValidVoterRetractVotes i =
|
|
ParameterBundle
|
|
{ proposalParameters =
|
|
ProposalParameters
|
|
{ proposalStatus = VotingReady
|
|
, retractVotes = True
|
|
}
|
|
, stakeParameters =
|
|
StakeParameters
|
|
{ numStakes = i
|
|
, stakeRole = Voter
|
|
, removeVoterLock = True
|
|
, removeCreatorLock = False
|
|
, alterOutputValue = False
|
|
}
|
|
, transactionParameters =
|
|
TransactionParameters
|
|
{ signedBy = Owner
|
|
, timeRange =
|
|
WhileVoting
|
|
}
|
|
}
|
|
|
|
mkValidDelegateeRetractVotes :: Integer -> ParameterBundle
|
|
mkValidDelegateeRetractVotes i =
|
|
let template = mkValidVoterRetractVotes i
|
|
in template
|
|
{ transactionParameters =
|
|
template.transactionParameters
|
|
{ signedBy = Delegatee
|
|
}
|
|
}
|
|
|
|
mkValidVoterCreatorRetractVotes :: Integer -> ParameterBundle
|
|
mkValidVoterCreatorRetractVotes i =
|
|
let template = mkValidVoterRetractVotes i
|
|
in template
|
|
{ stakeParameters =
|
|
template.stakeParameters
|
|
{ stakeRole = Both
|
|
}
|
|
}
|
|
|
|
mkValidCreatorRemoveLock :: Integer -> ParameterBundle
|
|
mkValidCreatorRemoveLock i =
|
|
let template = mkValidVoterRetractVotes i
|
|
in template
|
|
{ proposalParameters =
|
|
template.proposalParameters
|
|
{ proposalStatus = Finished
|
|
, retractVotes = False
|
|
}
|
|
, stakeParameters =
|
|
template.stakeParameters
|
|
{ stakeRole = Creator
|
|
, removeCreatorLock = True
|
|
}
|
|
, transactionParameters =
|
|
template.transactionParameters
|
|
{ timeRange = AfterVoting
|
|
}
|
|
}
|
|
|
|
mkValidVoterRemoveLockAfterVoting :: Integer -> ParameterBundle
|
|
mkValidVoterRemoveLockAfterVoting i =
|
|
let template = mkValidVoterRetractVotes i
|
|
in template
|
|
{ proposalParameters =
|
|
template.proposalParameters
|
|
{ proposalStatus = Finished
|
|
, retractVotes = False
|
|
}
|
|
, transactionParameters =
|
|
template.transactionParameters
|
|
{ timeRange = AfterVoting
|
|
}
|
|
}
|
|
|
|
mkRetractVotesWhileNotVoting :: Integer -> [ParameterBundle]
|
|
mkRetractVotesWhileNotVoting i =
|
|
let template = mkValidVoterRetractVotes i
|
|
in map
|
|
( \s ->
|
|
template
|
|
{ proposalParameters =
|
|
template.proposalParameters
|
|
{ proposalStatus = s
|
|
}
|
|
}
|
|
)
|
|
[Draft, Locked, Finished]
|
|
|
|
mkUnockIrrelevantStakes :: Integer -> ParameterBundle
|
|
mkUnockIrrelevantStakes i =
|
|
let template = mkValidVoterRetractVotes i
|
|
in template
|
|
{ stakeParameters =
|
|
template.stakeParameters
|
|
{ stakeRole = Irrelevant
|
|
, removeCreatorLock = True
|
|
}
|
|
}
|
|
|
|
mkRemoveCreatorLockBeforeFinished :: Integer -> [ParameterBundle]
|
|
mkRemoveCreatorLockBeforeFinished i =
|
|
let template = mkValidCreatorRemoveLock i
|
|
in map
|
|
( \s ->
|
|
template
|
|
{ proposalParameters =
|
|
template.proposalParameters
|
|
{ proposalStatus = s
|
|
}
|
|
}
|
|
)
|
|
[Draft, VotingReady, Locked]
|
|
|
|
mkCreatorRetractVotes :: Integer -> ParameterBundle
|
|
mkCreatorRetractVotes i =
|
|
let template = mkValidVoterRetractVotes i
|
|
in template
|
|
{ proposalParameters =
|
|
template.proposalParameters
|
|
{ proposalStatus = VotingReady
|
|
}
|
|
, stakeParameters =
|
|
template.stakeParameters
|
|
{ stakeRole = Creator
|
|
}
|
|
, transactionParameters =
|
|
template.transactionParameters
|
|
{ timeRange = WhileVoting
|
|
}
|
|
}
|
|
|
|
mkChangeOutputStakeValue :: Integer -> ParameterBundle
|
|
mkChangeOutputStakeValue i =
|
|
let template = mkValidVoterRetractVotes i
|
|
in template
|
|
{ stakeParameters =
|
|
template.stakeParameters
|
|
{ alterOutputValue = True
|
|
}
|
|
}
|