Merge pull request #155 from Liqwid-Labs/staging

Release 0.2.0
This commit is contained in:
emiflake 2022-08-13 21:41:13 +02:00 committed by GitHub
commit 4eb5fe2dee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
66 changed files with 13137 additions and 7997 deletions

View file

@ -1,78 +0,0 @@
on:
push:
paths:
- ".github/workflows/integrate.yaml"
- "**.hs"
- "**.nix"
- "flake.lock"
- "agora.cabal"
branches:
- main
- staging
pull_request:
paths:
- ".github/workflows/integrate.yaml"
- "**.hs"
- "**.nix"
- "flake.lock"
- "agora.cabal"
jobs:
flake:
runs-on: ubuntu-latest
strategy:
matrix:
tasks:
- agora
- formatCheck
- benchCheck
- agora-test
steps:
- uses: actions/checkout@v2.4.0
- uses: cachix/install-nix-action@v16
name: Set up Nix and IOHK caches
with:
nix_path: nixpkgs=channel:nixos-unstable
extra_nix_config: |
trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= iohk.cachix.org-1:DpRUyj7h7V830dp/i6Nti+NEO2/nhblbov/8MW7Rqoo= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
substituters = https://hydra.iohk.io https://iohk.cachix.org https://cache.nixos.org/
experimental-features = nix-command flakes
- uses: cachix/cachix-action@v10
with:
name: mlabs
authToken: ${{ secrets.CACHIX_KEY }}
- run: nix build .#checks.x86_64-linux.${{ matrix.tasks }}
name: Run '${{ matrix.tasks }}' from flake.nix
haddock:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.4.0
- uses: cachix/install-nix-action@v16
name: Set up Nix and IOHK caches
with:
nix_path: nixpkgs=channel:nixos-unstable
extra_nix_config: |
trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= iohk.cachix.org-1:DpRUyj7h7V830dp/i6Nti+NEO2/nhblbov/8MW7Rqoo= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
substituters = https://hydra.iohk.io https://iohk.cachix.org https://cache.nixos.org/
experimental-features = nix-command flakes
- uses: cachix/cachix-action@v10
with:
name: mlabs
authToken: ${{ secrets.CACHIX_KEY }}
- run: nix build .#packages.x86_64-linux.haddock
name: Run 'haddock' from flake.nix
# This publishes the haddock result to the branch 'gh-pages',
# which is set to automatically deploy to https://liqwid-labs.github.io/agora/.
- name: Publish Documentation
uses: peaceiris/actions-gh-pages@v3
if: github.ref == 'refs/heads/main'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./result/agora/html

View file

@ -2,6 +2,70 @@
This format is based on [Keep A Changelog](https://keepachangelog.com/en/1.0.0). This format is based on [Keep A Changelog](https://keepachangelog.com/en/1.0.0).
## Unreleased (Candidate for 1.0.0)
## 0.2.0 -- 2022-08-13
### Added
- Script exporting with `plutarch-script-export`
### Modified
- Bump plutarch to 1.2 and use `liqwid-nix` for flake derivation.
Included by [#150](https://github.com/Liqwid-Labs/agora/pull/150).
- Script building uses the lazy record `AgoraScripts` instead of explicit per-component parameters.
Included by [#150](https://github.com/Liqwid-Labs/agora/pull/150).
- Stake delegation.
Included by [#149](https://github.com/Liqwid-Labs/agora/pull/149).
- Fixed bug that checks the proposal thresholds in an incorrect way. Added negative tests for the governor scripts.
Included by [#146](https://github.com/Liqwid-Labs/agora/pull/146).
- Draft phase and cosigning for Proposals.
Included by [#136](https://github.com/Liqwid-Labs/agora/pull/136).
- Fixed bug with regards to moving from `VotingReady`.
Included by [#134](https://github.com/Liqwid-Labs/agora/pull/134).
- Fixed bug that made it impossible to create proposals. Added new stake locking mechanism for creating proposals.
Included by [#142](https://github.com/Liqwid-Labs/agora/pull/142).
NOTE: This changes the representation of the following types:
- `PProposalLock`
- `PStakeDatum`
- `PStakeRedeemer`
- `PProposalRedeemer`
- `PTreasuryRedeemer`
- `PGovernorDatum`
### Removed
- Side-stream utilies into `liqwid-Labs/liqwid-plutarch-extra`
- `Agora.MultiSig`--entire module.
- `scriptHashFromAddress` to `Plutarch.Api.V1.ScriptContext`.
- `findOutputsToAddress` to `Plutarch.Api.V1.ScriptContext`.
- `findTxOutDatum` to `Plutarch.Api.V1.ScriptContext`.
- `hasOnlyOneTokenOfCurrencySymbol` to `Plutarch.Api.V1.Value`.
- `mustBePJust` to `Plutarch.Extra.Maybe`.
- `mustBePDJust` to `Plutarch.Extra.Maybe`.
- `isScriptAddress` to `Plutarch.Api.V1.ScriptContext`.
- `isPubKey` to `Plutarch.Api.V1.ScriptContext`.
- `pisUniqBy'` to `Plutarch.Extra.List`.
- `pisUniq'` to `Plutarch.Extra.List`.
- `pon` to `Plutarch.Extra.Function`.
- `pbuiltinUncurry` to `Plutarch.Extra.Function`.
## 0.1.0 -- 2022-06-22 ## 0.1.0 -- 2022-06-22
### Added ### Added

View file

@ -1,13 +1,32 @@
# This really ought to be `/usr/bin/env bash`, but nix flakes don't like that. # This really ought to be `/usr/bin/env bash`, but nix flakes don't like that.
SHELL := /bin/sh SHELL := /bin/sh
.PHONY: hoogle format haddock usage tag format_nix format_haskell format_check lint ps_bridge bench bench_check scripts .PHONY: hoogle format haddock usage tag format_nix format_haskell format_check \
lint refactor ps_bridge bench bench_check scripts test build ci
AGORA_TARGETS := agora agora-bench agora-purescript-bridge agora-scripts agora-specs agora-test agora-testlib SOURCE_FILES := $(shell git ls-tree -r HEAD --full-tree --name-only)
SOURCE_FILES := $(wildcard $(SOURCE_FILES))
HASKELL_SOURCES := $(filter %.hs,$(SOURCE_FILES))
CABAL_SOURCES := $(filter %.cabal,$(SOURCE_FILES))
NIX_SOURCES := $(filter %.nix,$(SOURCE_FILES))
FORMAT_EXTENSIONS := -o -XQuasiQuotes -o -XTemplateHaskell -o -XTypeApplications \
-o -XImportQualifiedPost -o -XPatternSynonyms -o -XOverloadedRecordDot
HLINT_EXTS := -XQuasiQuotes
THREADS ?= 8
PS_BRIDGE_OUTPUT_DIR ?= agora-purescript-bridge/
BENCH_OUTPUT ?= bench.csv
TEST_CASE_TIMEOUT ?= 100
usage: usage:
@echo "usage: make <command> [OPTIONS]" @echo "usage: [env [<variable>=<value> ...]] make <command> [OPTIONS]"
@echo @echo
@echo "Available variables:"
@echo " THREADS -- The number of threads for building the project"
@echo " PS_BRIDGE_OUTPUT_DIR -- The output directory of the purescript bridge"
@echo " BENCH_OUTPUT -- The output file of the benchmark report"
@echo " TEST_CASE_TIMEOUT -- Timeout for individual tests. Default unit: s"
@echo
@echo "Available commands:" @echo "Available commands:"
@echo " hoogle -- Start local hoogle" @echo " hoogle -- Start local hoogle"
@echo " format -- Format the project" @echo " format -- Format the project"
@ -20,9 +39,14 @@ usage:
@echo " ps_bridge -- Generate purescript bridge files" @echo " ps_bridge -- Generate purescript bridge files"
@echo " bench -- Generate bench report bench.csv" @echo " bench -- Generate bench report bench.csv"
@echo " bench_check -- Check if bench report is up-to-date" @echo " bench_check -- Check if bench report is up-to-date"
@echo " scripts -- Export scripts to json files" @echo " scripts -- Run the agora script server (dev mode)"
@echo " ci -- Run all the CI checks"
hoogle: requires_nix_shell:
@ [ "$(IN_NIX_SHELL)" ] || echo "The $(MAKECMDGOALS) target must be run from inside a nix shell"
@ [ "$(IN_NIX_SHELL)" ] || (echo " run 'nix develop' first" && false)
hoogle: requires_nix_shell
pkill hoogle || true pkill hoogle || true
hoogle generate --local=haddock --database=hoo/local.hoo hoogle generate --local=haddock --database=hoo/local.hoo
hoogle server --local -p 8081 >> /dev/null & hoogle server --local -p 8081 >> /dev/null &
@ -30,45 +54,48 @@ hoogle:
format: format_haskell format_nix format: format_haskell format_nix
format_nix: format_nix: requires_nix_shell
git ls-tree -r HEAD --full-tree --name-only | grep -E '.*\.nix' | xargs nixpkgs-fmt nixpkgs-fmt $(NIX_SOURCES)
FORMAT_EXTENSIONS := -o -XQuasiQuotes -o -XTemplateHaskell -o -XTypeApplications -o -XImportQualifiedPost -o -XPatternSynonyms -o -XOverloadedRecordDot format_haskell: requires_nix_shell
format_haskell: fourmolu $(FORMAT_EXTENSIONS) -m inplace $(HASKELL_SOURCES)
find -name '*.hs' -not -path './dist-*/*' | xargs fourmolu $(FORMAT_EXTENSIONS) -m inplace cabal-fmt -i $(CABAL_SOURCES)
git ls-tree -r HEAD --full-tree --name-only | grep -E '.*\.cabal' | xargs cabal-fmt -i
format_check: format_check: requires_nix_shell
find -name '*.hs' \ fourmolu $(FORMAT_EXTENSIONS) -m check $(HASKELL_SOURCES)
-not -path './dist*/*' \ nixpkgs-fmt --check $(NIX_SOURCES)
-not -path './haddock/*' \ cabal-fmt --check $(CABAL_SOURCES)
| xargs fourmolu $(FORMAT_EXTENSIONS) -m check
haddock: haddock: requires_nix_shell
cabal haddock --haddock-html --haddock-hoogle --builddir=haddock cabal haddock --haddock-html --haddock-hoogle --builddir=haddock
tag: tag: requires_nix_shell
hasktags -x $(AGORA_TARGETS) hasktags -x $(HASKELL_SOURCES)
lint: lint: requires_nix_shell
hlint $(AGORA_TARGETS) hlint $(HLINT_EXTS) $(HASKELL_SOURCES)
PS_BRIDGE_OUTPUT_DIR := agora-purescript-bridge/ refactor: requires_nix_shell
ps_bridge: for src in $(HASKELL_SOURCES) ; do \
hlint $(HLINT_EXTS) --refactor --refactor-options='-i -s' $$src ;\
done
ps_bridge: requires_nix_shell
cabal run exe:agora-purescript-bridge -- -o $(PS_BRIDGE_OUTPUT_DIR) cabal run exe:agora-purescript-bridge -- -o $(PS_BRIDGE_OUTPUT_DIR)
bench: bench: requires_nix_shell
cabal run agora-bench cabal run agora-bench -- -o $(BENCH_OUTPUT)
BENCH_TMPDIR := $(shell mktemp -d) bench_check: requires_nix_shell
BENCH_TMPFILE := $(BENCH_TMPDIR)/bench.csv cabal -v0 new-run agora-bench | diff bench.csv -
bench_check:
(cabal run agora-bench -- -o "$(BENCH_TMPFILE)" \
|| $(bench) -o "$(BENCH_TMPFILE)") >> /dev/null
diff bench.csv $(BENCH_TMPFILE) \
|| (echo "bench.csv is outdated"; exit 1)
# TODO: do the clean-up even if `diff` fails.
rm -rf $(BENCH_TMPDIR)
scripts: scripts: requires_nix_shell
cabal run agora-scripts cabal run agora-scripts -- -c
test: requires_nix_shell
cabal test --test-options="--hide-successes -t $(TEST_CASE_TIMEOUT) -j$(THREADS)"
build: requires_nix_shell
cabal build -j$(THREADS)
ci: format_check lint build bench_check test haddock

View file

@ -1,5 +1,4 @@
# Agora :classical_building: # Agora :classical_building:
![integrate.yaml badge](https://github.com/Liqwid-Labs/agora/actions/workflows/integrate.yaml/badge.svg?branch=master)
Agora is a set of Plutus scripts that compose together to form a governance system. Agora is a set of Plutus scripts that compose together to form a governance system.
@ -13,7 +12,7 @@ Goals:
Non-goals: Non-goals:
- Agora is not a DAO. It doesn't have tokenomics or even a token. It is simply a library for governance. - Agora is not a DAO. It doesn't have tokenomics or even a token. It is simply a library for governance.
- Agora doesn't aim to provide any primitive tools for Plutus that are not governance-specific. For this, see [plutus-extra](https://github.com/Liqwid-Labs/plutus-extra/). - Agora doesn't aim to provide any primitive tools for Plutus that are not governance-specific. For this, see [liqwid-plutarch-extra](https://github.com/Liqwid-Labs/liqwid-plutarch-extra/).
## Project setup ## Project setup
@ -21,6 +20,18 @@ An up to date version of the [Nix package manager](nixos.org) (>=2.3) is require
Open a development shell with `nix develop` and build the project with `cabal build`. Those pained by the need to remember to enter a Nix shell may consider using [nix-direnv](https://github.com/nix-community/nix-direnv). Open a development shell with `nix develop` and build the project with `cabal build`. Those pained by the need to remember to enter a Nix shell may consider using [nix-direnv](https://github.com/nix-community/nix-direnv).
## `agora-scripts` HTTP export server
To use scripts in a frontend, you can use the `agora-scripts` executable which allows you to query them on-demand.
The CTL repo [`agora-offchain`](https://github.com/mlabs-haskell/agora-offchain) already has the setup prepared for this feature.
In order to run the server, simply run the following command:
```sh
cabal run agora-scripts -- --enable-cors-middleware
```
## Documentation ## Documentation
Documentation for Agora is hosted on Notion. You can find the specs [here](https://liqwid.notion.site/e85c09d2c9a542b19aac8dd3d6caa98b?v=d863219cd6a14082a661c4959cabd6e7). Documentation for Agora is hosted on Notion. You can find the specs [here](https://liqwid.notion.site/e85c09d2c9a542b19aac8dd3d6caa98b?v=d863219cd6a14082a661c4959cabd6e7).
@ -55,3 +66,9 @@ Please read [CONTRIBUTING.md](./CONTRIBUTING.md). Additionally, please follow th
- [ ] Rewards distribution - [ ] Rewards distribution
- [ ] Escrow staking pool solution - [ ] Escrow staking pool solution
### Available support channels info
You can find help, more information and ongoing discusion about the project here:
- The [Agora & Liqwid Libs Discord](https://discord.gg/yGkjxrYueB) - Most Agora discussion happens here.

View file

@ -8,7 +8,6 @@ import Data.ByteString.Short qualified as SBS
import Data.Csv (DefaultOrdered, ToNamedRecord, header, headerOrder, namedRecord, toNamedRecord, (.=)) import Data.Csv (DefaultOrdered, ToNamedRecord, header, headerOrder, namedRecord, toNamedRecord, (.=))
import Data.List (intercalate) import Data.List (intercalate)
import Data.Text (Text, pack) import Data.Text (Text, pack)
import GHC.Generics (Generic)
import Plutarch.Evaluate (evalScript) import Plutarch.Evaluate (evalScript)
import PlutusLedgerApi.V1 ( import PlutusLedgerApi.V1 (
ExBudget (ExBudget), ExBudget (ExBudget),

View file

@ -3,10 +3,10 @@ module Main (main) where
import Bench (specificationTreeToBenchmarks) import Bench (specificationTreeToBenchmarks)
import Data.Csv (EncodeOptions (encUseCrLf), defaultEncodeOptions, encodeDefaultOrderedByNameWith) import Data.Csv (EncodeOptions (encUseCrLf), defaultEncodeOptions, encodeDefaultOrderedByNameWith)
import Data.Text.Lazy.Encoding (decodeUtf8) import Data.Text.Lazy.Encoding (decodeUtf8)
import Data.Text.Lazy.IO as I (writeFile) import Data.Text.Lazy.IO as I (putStr, writeFile)
import Options (Options (..), parseOptions) import Options (Options (..), parseOptions)
import Prettyprinter (defaultLayoutOptions, layoutPretty, pretty) import Prettyprinter (defaultLayoutOptions, layoutPretty, pretty)
import Prettyprinter.Render.String (renderString) import Prettyprinter.Render.Text (renderLazy)
import Spec.AuthorityToken qualified as AuthorityToken import Spec.AuthorityToken qualified as AuthorityToken
import Spec.Effect.GovernorMutation qualified as GovernorMutation import Spec.Effect.GovernorMutation qualified as GovernorMutation
import Spec.Effect.TreasuryWithdrawal qualified as TreasuryWithdrawal import Spec.Effect.TreasuryWithdrawal qualified as TreasuryWithdrawal
@ -14,6 +14,7 @@ import Spec.Governor qualified as Governor
import Spec.Proposal qualified as Proposal import Spec.Proposal qualified as Proposal
import Spec.Stake qualified as Stake import Spec.Stake qualified as Stake
import Spec.Treasury qualified as Treasury import Spec.Treasury qualified as Treasury
import System.IO (hIsTerminalDevice, stdout)
import Test.Specification (group) import Test.Specification (group)
import Prelude import Prelude
@ -22,11 +23,14 @@ import Prelude
main :: IO () main :: IO ()
main = do main = do
options <- parseOptions options <- parseOptions
isTTY <- hIsTerminalDevice stdout
I.writeFile options.output $ mapM_ (`I.writeFile` csv) options.output
(decodeUtf8 . encodeDefaultOrderedByNameWith encodeOptions) res
mapM_ (putStrLn . renderString . layoutPretty defaultLayoutOptions . pretty) res I.putStr $
if isTTY
then prettified
else csv
where where
encodeOptions = encodeOptions =
defaultEncodeOptions defaultEncodeOptions
@ -49,3 +53,7 @@ main = do
, group "AuthorityToken" AuthorityToken.specs , group "AuthorityToken" AuthorityToken.specs
, group "Governor" Governor.specs , group "Governor" Governor.specs
] ]
csv = decodeUtf8 $ encodeDefaultOrderedByNameWith encodeOptions res
prettified = renderLazy $ layoutPretty defaultLayoutOptions $ pretty res

View file

@ -1,21 +1,22 @@
module Options (Options (..), parseOptions) where module Options (Options (..), parseOptions) where
import Control.Applicative (optional)
import Options.Applicative ((<**>)) import Options.Applicative ((<**>))
import Options.Applicative qualified as Opt import Options.Applicative qualified as Opt
newtype Options = Options newtype Options = Options
{ output :: FilePath { output :: Maybe FilePath
} }
outputOpt :: Opt.Parser FilePath outputOpt :: Opt.Parser (Maybe FilePath)
outputOpt = outputOpt =
Opt.strOption optional $
( Opt.long "output-path" Opt.strOption
<> Opt.short 'o' ( Opt.long "output-path"
<> Opt.metavar "OUTPUT_PATH" <> Opt.short 'o'
<> Opt.value "./bench.csv" <> Opt.metavar "OUTPUT_PATH"
<> Opt.help "The path of the bench report file." <> Opt.help "The path of the bench report file."
) )
benchOpt :: Opt.Parser Options benchOpt :: Opt.Parser Options
benchOpt = Options <$> outputOpt benchOpt = Options <$> outputOpt

View file

@ -15,7 +15,6 @@ import Agora.AuthorityToken qualified as AuthorityToken
import Agora.Effect.GovernorMutation qualified as GovernorMutation import Agora.Effect.GovernorMutation qualified as GovernorMutation
import Agora.Effect.TreasuryWithdrawal qualified as TreasuryWithdrawalEffect import Agora.Effect.TreasuryWithdrawal qualified as TreasuryWithdrawalEffect
import Agora.Governor qualified as Governor import Agora.Governor qualified as Governor
import Agora.MultiSig qualified as MultiSig
import Agora.Proposal qualified as Proposal import Agora.Proposal qualified as Proposal
import Agora.Stake qualified as Stake import Agora.Stake qualified as Stake
import Agora.Treasury qualified as Treasury import Agora.Treasury qualified as Treasury
@ -32,16 +31,12 @@ agoraTypes =
, mkSumType (Proxy @Proposal.ProposalVotes) , mkSumType (Proxy @Proposal.ProposalVotes)
, mkSumType (Proxy @Proposal.ProposalDatum) , mkSumType (Proxy @Proposal.ProposalDatum)
, mkSumType (Proxy @Proposal.ProposalRedeemer) , mkSumType (Proxy @Proposal.ProposalRedeemer)
, mkSumType (Proxy @Proposal.Proposal)
, -- Governor , -- Governor
mkSumType (Proxy @Governor.GovernorDatum) mkSumType (Proxy @Governor.GovernorDatum)
, mkSumType (Proxy @Governor.GovernorRedeemer) , mkSumType (Proxy @Governor.GovernorRedeemer)
, mkSumType (Proxy @Governor.Governor) , mkSumType (Proxy @Governor.Governor)
, -- MultiSig
mkSumType (Proxy @MultiSig.MultiSig)
, -- Stake , -- Stake
mkSumType (Proxy @Stake.Stake) mkSumType (Proxy @Stake.ProposalLock)
, mkSumType (Proxy @Stake.ProposalLock)
, mkSumType (Proxy @Stake.StakeRedeemer) , mkSumType (Proxy @Stake.StakeRedeemer)
, mkSumType (Proxy @Stake.StakeDatum) , mkSumType (Proxy @Stake.StakeDatum)
, -- Treasury , -- Treasury

164
agora-scripts/Main.hs Normal file
View file

@ -0,0 +1,164 @@
{-# LANGUAGE TemplateHaskell #-}
{- | Module : Main
Maintainer : emi@haskell.fyi
Description: Export scripts given configuration.
Export scripts given configuration.
-}
module Main (main) where
import Agora.Bootstrap qualified as Bootstrap
import Agora.Governor (Governor (..))
import Agora.SafeMoney (GTTag)
import Agora.Scripts qualified as Scripts
import Agora.Utils (CompiledMintingPolicy (..), CompiledValidator (..))
import Data.Aeson qualified as Aeson
import Data.Default (def)
import Data.Function ((&))
import Data.Tagged (Tagged)
import Data.Text (Text)
import Development.GitRev (gitBranch, gitHash)
import GHC.Generics qualified as GHC
import Plutarch (Config (..), TracingMode (DoTracing))
import PlutusLedgerApi.V1 (
MintingPolicy (getMintingPolicy),
TxOutRef,
Validator (getValidator),
)
import PlutusLedgerApi.V1.Value (AssetClass)
import ScriptExport.API (runServer)
import ScriptExport.Options (parseOptions)
import ScriptExport.ScriptInfo (ScriptInfo (..), mkPolicyInfo, mkScriptInfo, mkValidatorInfo)
import ScriptExport.Types (Builders, insertBuilder)
main :: IO ()
main =
parseOptions >>= runServer revision builders
where
-- This encodes the git revision of the server. It's useful for the caller
-- to be able to ensure they are compatible with it.
revision :: Text
revision = $(gitBranch) <> "@" <> $(gitHash)
{- | Builders for Agora scripts.
@since 0.2.0
-}
builders :: Builders
builders =
def
-- Agora scripts
& insertBuilder "governorPolicy" ((.governorPolicyInfo) . agoraScripts)
& insertBuilder "governorValidator" ((.governorValidatorInfo) . agoraScripts)
& insertBuilder "stakePolicy" ((.stakePolicyInfo) . agoraScripts)
& insertBuilder "stakeValidator" ((.stakeValidatorInfo) . agoraScripts)
& insertBuilder "proposalPolicy" ((.proposalPolicyInfo) . agoraScripts)
& insertBuilder "proposalValidator" ((.proposalValidatorInfo) . agoraScripts)
& insertBuilder "treasuryValidator" ((.treasuryValidatorInfo) . agoraScripts)
& insertBuilder "authorityTokenPolicy" ((.authorityTokenPolicyInfo) . agoraScripts)
-- Trivial scripts. These are useful for testing, but they likely aren't useful
-- to you if you are actually interested in deploying to mainnet.
& insertBuilder
"alwaysSucceedsPolicy"
(\() -> mkPolicyInfo $ plam $ \_ _ -> popaque (pconstant ()))
& insertBuilder
"alwaysSucceedsValidator"
(\() -> mkValidatorInfo $ plam $ \_ _ _ -> popaque (pconstant ()))
& insertBuilder
"neverSucceedsPolicy"
(\() -> mkPolicyInfo $ plam $ \_ _ -> perror)
& insertBuilder
"neverSucceedsValidator"
(\() -> mkValidatorInfo $ plam $ \_ _ _ -> perror)
{- | Create scripts from params.
@since 0.2.0
-}
agoraScripts :: ScriptParams -> AgoraScripts
agoraScripts params =
AgoraScripts
{ governorPolicyInfo = mkPolicyInfo' scripts.compiledGovernorPolicy
, governorValidatorInfo = mkValidatorInfo' scripts.compiledGovernorValidator
, stakePolicyInfo = mkPolicyInfo' scripts.compiledStakePolicy
, stakeValidatorInfo = mkValidatorInfo' scripts.compiledStakeValidator
, proposalPolicyInfo = mkPolicyInfo' scripts.compiledProposalPolicy
, proposalValidatorInfo = mkValidatorInfo' scripts.compiledProposalValidator
, treasuryValidatorInfo = mkValidatorInfo' scripts.compiledTreasuryValidator
, authorityTokenPolicyInfo = mkPolicyInfo' scripts.compiledAuthorityTokenPolicy
}
where
governor =
Agora.Governor.Governor
params.governorInitialSpend
params.gtClassRef
params.maximumCosigners
scripts = Bootstrap.agoraScripts plutarchConfig governor
{- | Params required for creating script export.
@since 0.2.0
-}
data ScriptParams where
ScriptParams ::
{ governorInitialSpend :: TxOutRef
, gtClassRef :: Tagged GTTag AssetClass
, maximumCosigners :: Integer
} ->
ScriptParams
deriving anyclass (Aeson.ToJSON, Aeson.FromJSON)
deriving stock (Show, Eq, GHC.Generic, Ord)
{- | Scripts that get exported.
@since 0.2.0
-}
data AgoraScripts = AgoraScripts
{ governorPolicyInfo :: ScriptInfo
, governorValidatorInfo :: ScriptInfo
, stakePolicyInfo :: ScriptInfo
, stakeValidatorInfo :: ScriptInfo
, proposalPolicyInfo :: ScriptInfo
, proposalValidatorInfo :: ScriptInfo
, treasuryValidatorInfo :: ScriptInfo
, authorityTokenPolicyInfo :: ScriptInfo
}
deriving anyclass
( -- | @since 0.2.0
Aeson.ToJSON
, -- | @since 0.2.0
Aeson.FromJSON
)
deriving stock
( -- | @since 0.2.0
Show
, -- | @since 0.2.0
Eq
, -- | @since 0.2.0
GHC.Generic
)
{- | Default plutarch configuration for compiling scripts.
TODO: we should have an option to control this.
@since 0.2.0
-}
plutarchConfig :: Config
plutarchConfig = Config {tracingMode = DoTracing}
{- | Turn a precompiled minting policy to a 'ScriptInfo'.
@since 0.2.0
-}
mkPolicyInfo' :: forall redeemer. CompiledMintingPolicy redeemer -> ScriptInfo
mkPolicyInfo' = mkScriptInfo . getMintingPolicy . getCompiledMintingPolicy
{- | Turn a precompiled validator to a 'ScriptInfo'.
@since 0.2.0
-}
mkValidatorInfo' :: forall redeemer datum. CompiledValidator datum redeemer -> ScriptInfo
mkValidatorInfo' = mkScriptInfo . getValidator . getCompiledValidator

View file

@ -1,45 +0,0 @@
{- |
Module : Options
Maintainer : emi@haskell.fyi
Description: Command line options for 'agora-scripts'.
Command line options for 'agora-scripts'.
-}
module Options (Options (..), parseOptions) where
import Options.Applicative ((<**>))
import Options.Applicative qualified as Opt
data Options = Options
{ config :: FilePath
, output :: FilePath
}
deriving stock (Show, Eq)
opt :: Opt.Parser Options
opt =
Options
<$> Opt.strOption
( Opt.long "config"
<> Opt.short 'c'
<> Opt.metavar "CONFIG_PATH"
<> Opt.value "./agora-scripts/agora-params.json"
<> Opt.help "The path where the script configuration is."
)
<*> Opt.strOption
( Opt.long "output"
<> Opt.short 'o'
<> Opt.metavar "OUTPUT_PATH"
<> Opt.value "./agora-scripts/agora-scripts.json"
<> Opt.help "Output where generated scripts will be."
)
parseOptions :: IO Options
parseOptions = Opt.execParser p
where
p =
Opt.info
(opt <**> Opt.helper)
( Opt.fullDesc
<> Opt.progDesc "Generate Agora scripts for off-chain use."
)

View file

@ -1,121 +0,0 @@
{-# LANGUAGE TemplateHaskell #-}
{- |
Module : Scripts
Maintainer : emi@haskell.fyi
Description: Export scripts given configuration.
Export scripts given configuration.
-}
module Main (main) where
import Agora.AuthorityToken (AuthorityToken, authorityTokenPolicy)
import Agora.Governor (Governor (Governor))
import Agora.Governor qualified as Governor
import Agora.Governor.Scripts (
authorityTokenFromGovernor,
authorityTokenSymbolFromGovernor,
governorPolicy,
governorValidator,
proposalFromGovernor,
stakeFromGovernor,
)
import Agora.Proposal (Proposal)
import Agora.Proposal.Scripts (proposalPolicy, proposalValidator)
import Agora.SafeMoney (GTTag)
import Agora.ScriptInfo (PolicyInfo, ValidatorInfo, mkPolicyInfo, mkValidatorInfo)
import Agora.Stake (Stake)
import Agora.Stake.Scripts (stakePolicy, stakeValidator)
import Agora.Treasury (treasuryValidator)
import Control.Monad ((>=>))
import Data.Aeson qualified as Aeson
import Data.Tagged (Tagged)
import Development.GitRev (gitBranch, gitHash)
import GHC.Generics qualified as GHC
import Options (Options (..), parseOptions)
import Plutarch.Api.V1 (mintingPolicySymbol, mkMintingPolicy)
import PlutusLedgerApi.V1 (TxOutRef)
import PlutusLedgerApi.V1.Value (AssetClass, CurrencySymbol)
import PlutusLedgerApi.V1.Value qualified as Value
import System.Exit (exitFailure)
import Text.Printf (printf)
-- | Params required for creating script export.
data ScriptParams = ScriptParams
{ governorInitialSpend :: TxOutRef
, gtClassRef :: Tagged GTTag AssetClass
, maximumCosigners :: Integer
}
deriving anyclass (Aeson.ToJSON, Aeson.FromJSON)
deriving stock (Show, Eq, GHC.Generic)
-- | Scripts that get exported.
data AgoraScripts = AgoraScripts
{ gitRev :: String
, governorPolicyInfo :: PolicyInfo
, governorValidatorInfo :: ValidatorInfo
, stakePolicyInfo :: PolicyInfo
, stakeValidatorInfo :: ValidatorInfo
, proposalPolicyInfo :: PolicyInfo
, proposalValidatorInfo :: ValidatorInfo
, treasuryValidatorInfo :: ValidatorInfo
, authorityTokenPolicyInfo :: PolicyInfo
}
deriving anyclass (Aeson.ToJSON, Aeson.FromJSON)
deriving stock (Show, Eq, GHC.Generic)
main :: IO ()
main = do
options <- parseOptions
params <-
Aeson.eitherDecodeFileStrict @ScriptParams options.config
>>= either (putStrLn >=> const exitFailure) pure
let scripts = agoraScripts params
Aeson.encodeFile options.output scripts
printf "Done! Wrote to %s\n" options.output
-- | Create scripts from params.
agoraScripts :: ScriptParams -> AgoraScripts
agoraScripts params =
AgoraScripts
{ gitRev = revision
, governorPolicyInfo = mkPolicyInfo (governorPolicy governor)
, governorValidatorInfo = mkValidatorInfo (governorValidator governor)
, stakePolicyInfo = mkPolicyInfo (stakePolicy params.gtClassRef)
, stakeValidatorInfo = mkValidatorInfo (stakeValidator stake)
, proposalPolicyInfo = mkPolicyInfo (proposalPolicy governorSTAssetClass)
, proposalValidatorInfo = mkValidatorInfo (proposalValidator proposal)
, treasuryValidatorInfo = mkValidatorInfo (treasuryValidator authorityTokenSymbol)
, authorityTokenPolicyInfo = mkPolicyInfo (authorityTokenPolicy authorityToken)
}
where
revision :: String
revision = $(gitBranch) <> "@" <> $(gitHash)
governor :: Governor
governor =
Governor
{ Governor.gstOutRef = params.governorInitialSpend
, Governor.gtClassRef = params.gtClassRef
, Governor.maximumCosigners = params.maximumCosigners
}
authorityToken :: AuthorityToken
authorityToken = authorityTokenFromGovernor governor
authorityTokenSymbol :: CurrencySymbol
authorityTokenSymbol = authorityTokenSymbolFromGovernor governor
governorSTAssetClass :: AssetClass
governorSTAssetClass =
Value.assetClass (mintingPolicySymbol $ mkMintingPolicy $ governorPolicy governor) ""
proposal :: Proposal
proposal = proposalFromGovernor governor
stake :: Stake
stake = stakeFromGovernor governor

View file

@ -1,11 +0,0 @@
{
"governorInitialSpend": {
"txOutRefId": "7be688c61c209dd7a9f4948090db0b031b11a8850b0ee4695f786fea85fbfdee",
"txOutRefIdx": 0
},
"gtClassRef": [
"",
""
],
"maximumCosigners": 5
}

View file

@ -114,8 +114,10 @@ genInput = do
val <- genSingletonValue val <- genSingletonValue
return $ return $
input $ input $
credential cred mconcat
. withValue val [ credential cred
, withValue val
]
genOutput :: Builder a => Gen a genOutput :: Builder a => Gen a
genOutput = do genOutput = do
@ -123,8 +125,10 @@ genOutput = do
val <- genSingletonValue val <- genSingletonValue
return $ return $
output $ output $
credential cred mconcat
. withValue val [ credential cred
, withValue val
]
genOutRef :: Gen TxOutRef genOutRef :: Gen TxOutRef
genOutRef = do genOutRef = do

View file

@ -7,7 +7,7 @@ Property model and tests for 'Governor' related functions
-} -}
module Property.Governor (props) where module Property.Governor (props) where
import Agora.Governor (GovernorDatum (..), governorDatumValid) import Agora.Governor (Governor (gstOutRef), GovernorDatum (..), pisGovernorDatumValid)
import Agora.Governor.Scripts (governorPolicy) import Agora.Governor.Scripts (governorPolicy)
import Agora.Proposal ( import Agora.Proposal (
ProposalId (ProposalId), ProposalId (ProposalId),
@ -30,6 +30,7 @@ import Plutarch.Context (
output, output,
script, script,
withDatum, withDatum,
withMinting,
withOutRef, withOutRef,
withValue, withValue,
) )
@ -43,6 +44,7 @@ import PlutusLedgerApi.V1.Value (assetClassValue)
import Property.Generator (genInput, genOutput) import Property.Generator (genInput, genOutput)
import Sample.Shared ( import Sample.Shared (
govAssetClass, govAssetClass,
govSymbol,
govValidatorHash, govValidatorHash,
governor, governor,
gstUTXORef, gstUTXORef,
@ -62,8 +64,6 @@ data GovernorDatumCases
= ExecuteLE0 = ExecuteLE0
| CreateLE0 | CreateLE0
| VoteLE0 | VoteLE0
| CreateLEVote
| ExecuteLVote
| Correct | Correct
deriving stock (Eq, Show) deriving stock (Eq, Show)
@ -72,8 +72,6 @@ instance Universe GovernorDatumCases where
[ ExecuteLE0 [ ExecuteLE0
, CreateLE0 , CreateLE0
, VoteLE0 , VoteLE0
, CreateLEVote
, ExecuteLVote
, Correct , Correct
] ]
@ -87,15 +85,13 @@ instance Finite GovernorDatumCases where
-} -}
governorDatumValidProperty :: Property governorDatumValidProperty :: Property
governorDatumValidProperty = governorDatumValidProperty =
classifiedPropertyNative gen (const []) expected classifier governorDatumValid classifiedPropertyNative gen (const []) expected classifier pisGovernorDatumValid
where where
classifier :: GovernorDatum -> GovernorDatumCases classifier :: GovernorDatum -> GovernorDatumCases
classifier (proposalThresholds -> ProposalThresholds e c v) classifier (proposalThresholds -> ProposalThresholds e c v)
| e < 0 = ExecuteLE0 | e < 0 = ExecuteLE0
| c < 0 = CreateLE0 | c < 0 = CreateLE0
| v < 0 = VoteLE0 | v < 0 = VoteLE0
| c > v = CreateLEVote
| v >= e = ExecuteLVote
| otherwise = Correct | otherwise = Correct
expected :: GovernorDatum -> Maybe Bool expected :: GovernorDatum -> Maybe Bool
@ -106,7 +102,7 @@ governorDatumValidProperty =
thres <- genProposalThresholds c thres <- genProposalThresholds c
let timing = ProposalTimingConfig 0 0 0 0 let timing = ProposalTimingConfig 0 0 0 0
return $ GovernorDatum thres (ProposalId 0) timing (MaxTimeRangeWidth 0) return $ GovernorDatum thres (ProposalId 0) timing (MaxTimeRangeWidth 1) 3
where where
taggedInteger p = Tagged <$> chooseInteger p taggedInteger p = Tagged <$> chooseInteger p
genProposalThresholds :: GovernorDatumCases -> Gen ProposalThresholds genProposalThresholds :: GovernorDatumCases -> Gen ProposalThresholds
@ -127,16 +123,6 @@ governorDatumValidProperty =
VoteLE0 -> VoteLE0 ->
-- vote < 0 -- vote < 0
return $ ProposalThresholds execute create le0 return $ ProposalThresholds execute create le0
CreateLEVote -> do
-- c > vote
nv <- taggedInteger (0, untag create - 1)
ne <- taggedInteger (untag nv + 1, 1000000000)
return $ ProposalThresholds ne create nv
ExecuteLVote -> do
-- vote >= execute
ne <- taggedInteger (0, untag vote)
nc <- taggedInteger (0, untag vote)
return $ ProposalThresholds ne nc vote
Correct -> do Correct -> do
-- c <= vote < execute -- c <= vote < execute
nv <- taggedInteger (0, untag execute - 1) nv <- taggedInteger (0, untag execute - 1)
@ -171,7 +157,13 @@ governorMintingProperty =
-} -}
gst = assetClassValue govAssetClass 1 gst = assetClassValue govAssetClass 1
mintAmount x = mint . mconcat $ replicate x gst mintAmount x = mint . mconcat $ replicate x gst
outputToGov = output $ script govValidatorHash . withValue gst . withDatum govDatum outputToGov =
output $
mconcat
[ script govValidatorHash
, withValue gst
, withDatum govDatum
]
referencedInput = input $ withOutRef gstUTXORef referencedInput = input $ withOutRef gstUTXORef
govDatum :: GovernorDatum govDatum :: GovernorDatum
@ -181,6 +173,7 @@ governorMintingProperty =
, nextProposalId = ProposalId 0 , nextProposalId = ProposalId 0
, proposalTimings = def , proposalTimings = def
, createProposalTimeRangeMaxWidth = def , createProposalTimeRangeMaxWidth = def
, maximumProposalsPerStake = 3
} }
gen :: GovernorPolicyCases -> Gen ScriptContext gen :: GovernorPolicyCases -> Gen ScriptContext
@ -196,7 +189,7 @@ governorMintingProperty =
GovernorOutputNotFound -> referencedInput <> mintAmount 1 GovernorOutputNotFound -> referencedInput <> mintAmount 1
GovernorPolicyCorrect -> referencedInput <> outputToGov <> mintAmount 1 GovernorPolicyCorrect -> referencedInput <> outputToGov <> mintAmount 1
return . buildMintingUnsafe $ inputs <> outputs <> comp return . buildMintingUnsafe $ inputs <> outputs <> comp <> withMinting govSymbol
expected :: ScriptContext -> Maybe () expected :: ScriptContext -> Maybe ()
expected sc = expected sc =
@ -208,7 +201,7 @@ governorMintingProperty =
opaqueToUnit = plam $ \_ -> pconstant () opaqueToUnit = plam $ \_ -> pconstant ()
actual :: Term s (PScriptContext :--> PUnit) actual :: Term s (PScriptContext :--> PUnit)
actual = plam $ \sc -> opaqueToUnit #$ governorPolicy governor # pforgetData (pconstantData ()) # sc actual = plam $ \sc -> opaqueToUnit #$ governorPolicy governor.gstOutRef # pforgetData (pconstantData ()) # sc
classifier :: ScriptContext -> GovernorPolicyCases classifier :: ScriptContext -> GovernorPolicyCases
classifier sc classifier sc

View file

@ -1,107 +0,0 @@
{- |
Module : Property.MultiSig
Maintainer : seungheon.ooh@gmail.com
Description: Property tests for 'MultiSig' functions
Property model and tests for 'MultiSig' functions
-}
module Property.MultiSig (props) where
import Agora.MultiSig (
MultiSig (MultiSig),
PMultiSig,
pvalidatedByMultisig,
)
import Data.Tagged (Tagged (Tagged))
import Data.Universe (Finite (..), Universe (..))
import Plutarch.Api.V1 (PScriptContext)
import Plutarch.Context
import Plutarch.Extra.TermCont (pletC)
import PlutusLedgerApi.V1 (
ScriptContext (..),
ScriptPurpose (..),
TxInfo (txInfoSignatories),
TxOutRef (..),
)
import Property.Generator (genPubKeyHash)
import Test.Tasty (TestTree)
import Test.Tasty.Plutarch.Property (classifiedPropertyNative)
import Test.Tasty.QuickCheck (
Gen,
Property,
chooseInt,
listOf,
testProperty,
vectorOf,
)
-- | Model for testing multisigs.
type MultiSigModel = (MultiSig, ScriptContext)
-- | Propositions that may hold true of a `MultiSigModel`.
data MultiSigProp
= -- | Sufficient number of signatories in the script context.
MeetsMinSigs
| -- | Insufficient number of signatories in the script context.
DoesNotMeetMinSigs
deriving stock (Eq, Show, Ord)
instance Universe MultiSigProp where
universe = [MeetsMinSigs, DoesNotMeetMinSigs]
instance Finite MultiSigProp where
universeF = universe
cardinality = Tagged 2
-- | Generate model with given proposition.
genMultiSigProp :: MultiSigProp -> Gen MultiSigModel
genMultiSigProp prop = do
size <- chooseInt (4, 20)
pkhs <- vectorOf size genPubKeyHash
minSig <- chooseInt (1, length pkhs)
othersigners <- take 20 <$> listOf genPubKeyHash
let ms = MultiSig pkhs (toInteger minSig)
n <- case prop of
MeetsMinSigs -> chooseInt (minSig, length pkhs)
DoesNotMeetMinSigs -> chooseInt (0, minSig - 1)
let builder :: BaseBuilder
builder = mconcat $ signedWith <$> take n pkhs <> othersigners
txinfo = buildTxInfoUnsafe builder
pure (ms, ScriptContext txinfo (Spending (TxOutRef "" 0)))
-- | Classify model into propositions.
classifyMultiSigProp :: MultiSigModel -> MultiSigProp
classifyMultiSigProp (MultiSig keys (fromIntegral -> minsig), ctx)
| minsig <= length signer = MeetsMinSigs
| otherwise = DoesNotMeetMinSigs
where
signer = filter (`elem` keys) $ txInfoSignatories . scriptContextTxInfo $ ctx
-- | Shrinker. Not used.
shrinkMultiSigProp :: MultiSigModel -> [MultiSigModel]
shrinkMultiSigProp = const []
-- | Expected behavior of @pvalidatedByMultisig@.
expectedHs :: MultiSigModel -> Maybe Bool
expectedHs model = case classifyMultiSigProp model of
MeetsMinSigs -> Just True
_ -> Just False
-- | Actual implementation of @pvalidatedByMultisig@.
actual :: Term s (PBuiltinPair PMultiSig PScriptContext :--> PBool)
actual = plam $ \x -> unTermCont $ do
ms <- pletC $ pfstBuiltin # x
sc <- pletC $ psndBuiltin # x
pure $ pvalidatedByMultisig # ms # (pfield @"txInfo" # sc)
-- | Proposed property.
prop :: Property
prop = classifiedPropertyNative genMultiSigProp shrinkMultiSigProp expectedHs classifyMultiSigProp actual
props :: [TestTree]
props =
[ testProperty "MultiSig property" prop
]

View file

@ -17,6 +17,7 @@ import Agora.Effect.GovernorMutation (
) )
import Agora.Governor (GovernorDatum (..)) import Agora.Governor (GovernorDatum (..))
import Agora.Proposal (ProposalId (..), ProposalThresholds (..)) import Agora.Proposal (ProposalId (..), ProposalThresholds (..))
import Agora.Utils (validatorHashToTokenName)
import Data.Default.Class (Default (def)) import Data.Default.Class (Default (def))
import Data.Tagged (Tagged (..)) import Data.Tagged (Tagged (..))
import Plutarch.Api.V1 (mkValidator, validatorHash) import Plutarch.Api.V1 (mkValidator, validatorHash)
@ -24,7 +25,6 @@ import PlutusLedgerApi.V1 (
Address, Address,
Datum (..), Datum (..),
ToData (..), ToData (..),
TokenName (..),
TxInInfo (..), TxInInfo (..),
TxInfo (..), TxInfo (..),
TxOut (..), TxOut (..),
@ -40,10 +40,11 @@ import PlutusLedgerApi.V1.Value qualified as Value (
singleton, singleton,
) )
import Sample.Shared ( import Sample.Shared (
agoraScripts,
authorityTokenSymbol, authorityTokenSymbol,
deterministicTracingConfing,
govAssetClass, govAssetClass,
govValidatorAddress, govValidatorAddress,
governor,
minAda, minAda,
signer, signer,
) )
@ -51,7 +52,7 @@ import Test.Util (datumPair, toDatumHash)
-- | The effect validator instance. -- | The effect validator instance.
effectValidator :: Validator effectValidator :: Validator
effectValidator = mkValidator $ mutateGovernorValidator governor effectValidator = mkValidator deterministicTracingConfing $ mutateGovernorValidator agoraScripts
-- | The hash of the validator instance. -- | The hash of the validator instance.
effectValidatorHash :: ValidatorHash effectValidatorHash :: ValidatorHash
@ -65,17 +66,15 @@ effectValidatorAddress = scriptHashAddress effectValidatorHash
atAssetClass :: AssetClass atAssetClass :: AssetClass
atAssetClass = assetClass authorityTokenSymbol tokenName atAssetClass = assetClass authorityTokenSymbol tokenName
where where
-- TODO: use 'validatorHashToTokenName' tokenName = validatorHashToTokenName effectValidatorHash
ValidatorHash bs = effectValidatorHash
tokenName = TokenName bs
-- | The mock reference of the governor state UTXO. -- | The mock reference of the governor state UTXO.
govRef :: TxOutRef govRef :: TxOutRef
govRef = TxOutRef "614481d2159bfb72350222d61fce17e548e0fc00e5a1f841ff1837c431346ce7" 1 govRef = TxOutRef "1475e1ee22330dfc55430980e5a6b100ec9d9249bb4b462256a79559" 1
-- | The mock reference of the effect UTXO. -- | The mock reference of the effect UTXO.
effectRef :: TxOutRef effectRef :: TxOutRef
effectRef = TxOutRef "c31164dc11835de7eb6187f67d0e1a19c1dfc0786a456923eef5043189cdb578" 1 effectRef = TxOutRef "a302d327d8e5553d50b9d017475369753f723d7e999ac1b68da8ad52" 1
-- | The input effect datum in 'mkEffectTransaction'. -- | The input effect datum in 'mkEffectTransaction'.
mkEffectDatum :: GovernorDatum -> MutateGovernorDatum mkEffectDatum :: GovernorDatum -> MutateGovernorDatum
@ -106,6 +105,7 @@ mkEffectTxInfo newGovDatum =
, nextProposalId = ProposalId 0 , nextProposalId = ProposalId 0
, proposalTimings = def , proposalTimings = def
, createProposalTimeRangeMaxWidth = def , createProposalTimeRangeMaxWidth = def
, maximumProposalsPerStake = 3
} }
governorInputDatum :: Datum governorInputDatum :: Datum
governorInputDatum = Datum $ toBuiltinData governorInputDatum' governorInputDatum = Datum $ toBuiltinData governorInputDatum'
@ -158,7 +158,7 @@ mkEffectTxInfo newGovDatum =
, txInfoValidRange = Interval.always , txInfoValidRange = Interval.always
, txInfoSignatories = [signer] , txInfoSignatories = [signer]
, txInfoData = datumPair <$> [governorInputDatum, governorOutputDatum, effectInputDatum] , txInfoData = datumPair <$> [governorInputDatum, governorOutputDatum, effectInputDatum]
, txInfoId = "4dae3806cc69615b721d52ed09b758f43f25a8f39b7934d6b28514caf71f5f7b" , txInfoId = "74c75505691e7baa981fa80e50b9b7e88dbe1eda67d4f062d89d203b"
} }
validNewGovernorDatum :: GovernorDatum validNewGovernorDatum :: GovernorDatum
@ -168,6 +168,7 @@ validNewGovernorDatum =
, nextProposalId = ProposalId 42 , nextProposalId = ProposalId 42
, proposalTimings = def , proposalTimings = def
, createProposalTimeRangeMaxWidth = def , createProposalTimeRangeMaxWidth = def
, maximumProposalsPerStake = 3
} }
invalidNewGovernorDatum :: GovernorDatum invalidNewGovernorDatum :: GovernorDatum
@ -180,4 +181,5 @@ invalidNewGovernorDatum =
, nextProposalId = ProposalId 42 , nextProposalId = ProposalId 42
, proposalTimings = def , proposalTimings = def
, createProposalTimeRangeMaxWidth = def , createProposalTimeRangeMaxWidth = def
, maximumProposalsPerStake = 3
} }

View file

@ -23,17 +23,14 @@ import Agora.Effect.TreasuryWithdrawal (
TreasuryWithdrawalDatum (TreasuryWithdrawalDatum), TreasuryWithdrawalDatum (TreasuryWithdrawalDatum),
treasuryWithdrawalValidator, treasuryWithdrawalValidator,
) )
import Crypto.Hash qualified as Crypto import Data.Default (def)
import Data.ByteArray qualified as BA
import Data.ByteString qualified as BS
import Data.ByteString.Char8 qualified as C (pack)
import Plutarch.Api.V1 (mkValidator, validatorHash) import Plutarch.Api.V1 (mkValidator, validatorHash)
import PlutusLedgerApi.V1 ( import PlutusLedgerApi.V1 (
Address (Address), Address (Address),
Credential (..), Credential (..),
CurrencySymbol (CurrencySymbol), CurrencySymbol,
DatumHash (DatumHash), DatumHash (DatumHash),
PubKeyHash (PubKeyHash), PubKeyHash,
ScriptContext (..), ScriptContext (..),
ScriptPurpose (Spending), ScriptPurpose (Spending),
TokenName (TokenName), TokenName (TokenName),
@ -56,29 +53,26 @@ import PlutusLedgerApi.V1 (
Validator, Validator,
ValidatorHash (ValidatorHash), ValidatorHash (ValidatorHash),
Value, Value,
toBuiltin,
) )
import PlutusLedgerApi.V1.Interval qualified as Interval (always) import PlutusLedgerApi.V1.Interval qualified as Interval (always)
import PlutusLedgerApi.V1.Value qualified as Value (singleton) import PlutusLedgerApi.V1.Value qualified as Value (singleton)
import Test.Util (scriptCredentials, userCredentials)
-- | A sample Currency Symbol. -- | A sample Currency Symbol.
currSymbol :: CurrencySymbol currSymbol :: CurrencySymbol
currSymbol = CurrencySymbol "12312099" currSymbol = "9c04a69c7133e26061fe5a15adaf4f79cd51e47ef22a2e3c91a36f04"
-- | A sample 'PubKeyHash'. -- | A sample 'PubKeyHash'.
signer :: PubKeyHash signer :: PubKeyHash
signer = "8a30896c4fd5e79843e4ca1bd2cdbaa36f8c0bc3be7401214142019c" signer = "8a30896c4fd5e79843e4ca1bd2cdbaa36f8c0bc3be7401214142019c"
blake2b_224 :: BS.ByteString -> BS.ByteString
blake2b_224 = BS.pack . BA.unpack . Crypto.hashWith Crypto.Blake2b_224
-- | List of users who the effect will pay to. -- | List of users who the effect will pay to.
users :: [Credential] users :: [Credential]
users = PubKeyCredential . PubKeyHash . toBuiltin . blake2b_224 . C.pack . show <$> ([1 ..] :: [Integer]) users = userCredentials
-- | List of users who the effect will pay to. -- | List of users who the effect will pay to.
treasuries :: [Credential] treasuries :: [Credential]
treasuries = ScriptCredential . ValidatorHash . toBuiltin . blake2b_224 . C.pack . show <$> ([1 ..] :: [Integer]) treasuries = scriptCredentials
inputGAT :: TxInInfo inputGAT :: TxInInfo
inputGAT = inputGAT =
@ -154,7 +148,7 @@ buildReceiversOutputFromDatum (TreasuryWithdrawalDatum xs _) = f <$> xs
-- | Effect validator instance. -- | Effect validator instance.
validator :: Validator validator :: Validator
validator = mkValidator $ treasuryWithdrawalValidator currSymbol validator = mkValidator def $ treasuryWithdrawalValidator currSymbol
-- | 'TokenName' that represents the hash of the 'Agora.Stake.Stake' validator. -- | 'TokenName' that represents the hash of the 'Agora.Stake.Stake' validator.
validatorHashTN :: TokenName validatorHashTN :: TokenName

View file

@ -1,461 +0,0 @@
{- |
Module : Spec.Sample.Governor
Maintainer : connor@mlabs.city
Description: Sample based testing for Governor utxos
This module tests primarily the happy path for Governor interactions
-}
module Sample.Governor (
createProposal,
mutateState,
mintGATs,
mintGST,
) where
import Agora.Effect.NoOp (noOpValidator)
import Agora.Governor (GovernorDatum (..), getNextProposalId)
import Agora.Proposal (
ProposalDatum (..),
ProposalId (..),
ProposalStatus (..),
ProposalVotes (..),
ResultTag (..),
emptyVotesFor,
)
import Agora.Proposal qualified as P (ProposalDatum (proposalId))
import Agora.Proposal.Time (
ProposalStartingTime (ProposalStartingTime),
ProposalTimingConfig (..),
)
import Agora.Stake (ProposalLock (..), Stake (..), StakeDatum (..))
import Data.Default.Class (Default (def))
import Data.Tagged (Tagged (..), untag)
import Plutarch.Api.V1 (mkValidator, validatorHash)
import Plutarch.Context (
MintingBuilder,
SpendingBuilder,
buildMintingUnsafe,
buildSpendingUnsafe,
fee,
input,
mint,
output,
script,
signedWith,
timeRange,
txId,
withDatum,
withRefIndex,
withSpending,
withTxId,
withValue,
)
import PlutusLedgerApi.V1 (
BuiltinData (BuiltinData),
Data (I),
Datum (Datum),
ScriptContext,
TokenName (TokenName),
TxOutRef (txOutRefId),
Validator,
ValidatorHash (..),
)
import PlutusLedgerApi.V1.Value (AssetClass (AssetClass))
import PlutusLedgerApi.V1.Value qualified as Value (
assetClassValue,
singleton,
)
import PlutusTx.AssocMap qualified as AssocMap (
empty,
fromList,
singleton,
)
import Sample.Shared (
authorityTokenSymbol,
govAssetClass,
govValidatorHash,
gstUTXORef,
minAda,
proposalPolicySymbol,
proposalStartingTimeFromTimeRange,
proposalValidatorHash,
signer,
signer2,
stake,
stakeAssetClass,
stakeValidatorHash,
)
import Test.Util (closedBoundedInterval, toDatumHash)
-- | Unit datum
unitDatum :: Datum
unitDatum = Datum . BuiltinData $ I 0 -- This could be anything, really. It doesn't matter.
{- | A valid 'ScriptContext' for minting GST.
- Only the minting policy will be ran in the transaction.
- An arbitrary UTXO is spent to create the token.
- We call this the "witness" UTXO.
- This UTXO is referenced in the 'Agora.Governor.Governor' parameter
- The minting policy should only be ran once its life time,
cause the GST cannot be minted twice or burnt.
- The output UTXO must carry a valid 'GovernorDatum'.
- It's worth noticing that the transaction should send the GST to the governor validator,
but unfortunately we can't check it in the policy. The GST will stay at the address of
the governor validator forever once the token is under control of the said validator.
TODO: tag the output UTXO with the target address.
-}
mintGST :: ScriptContext
mintGST =
let gst = Value.assetClassValue govAssetClass 1
governorOutputDatum :: GovernorDatum
governorOutputDatum =
GovernorDatum
{ proposalThresholds = def
, nextProposalId = ProposalId 0
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
}
witness :: ValidatorHash
witness = "a926a9a72a0963f428e3252caa8354e655603996fb8892d6b8323fd072345924"
builder :: MintingBuilder
builder =
mconcat
[ txId "90906d3e6b4d6dec2e747dcdd9617940ea8358164c7244694cfa39dec18bd9d4"
, signedWith signer
, mint gst
, input $
script witness
. withTxId (txOutRefId gstUTXORef)
. withRefIndex 0
, output $
script govValidatorHash
. withValue (gst <> minAda)
. withDatum governorOutputDatum
]
in buildMintingUnsafe builder
{- | A valid script context to create a proposal.
Three component will run in the transaction:
TODO: mention redeemers
- Governor validator
- Stake validator
- Proposal policy
The components will ensure:
- The governor state UTXO is spent
- A new UTXO is paid back to governor validator, which carries the GST.
- The proposal id in the state datum is advanced.
- A new UTXO is sent to the proposal validator
- The UTXO contains a newly minted proposal state token.
- It also carries a legal proposal state datum, whose status is set to 'Agora.Proposal.Draft'.
- A stake is spent to create a proposal
- The stake owner must sign the transaction.
- The output stake must paid back to the stake validator.
- The output stake is locked by the newly created proposal.
-}
createProposal :: ScriptContext
createProposal =
let pst = Value.singleton proposalPolicySymbol "" 1
gst = Value.assetClassValue govAssetClass 1
sst = Value.assetClassValue stakeAssetClass 1
stackedGTs = 424242424242
thisProposalId = ProposalId 0
governorInputDatum :: GovernorDatum
governorInputDatum =
GovernorDatum
{ proposalThresholds = def
, nextProposalId = thisProposalId
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
}
effects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
]
proposalDatum :: ProposalDatum
proposalDatum =
ProposalDatum
{ P.proposalId = ProposalId 0
, effects = effects
, status = Draft
, cosigners = [signer]
, thresholds = def
, votes = emptyVotesFor effects
, timingConfig = def
, startingTime = proposalStartingTimeFromTimeRange validTimeRange
}
stakeInputDatum :: StakeDatum
stakeInputDatum =
StakeDatum
{ stakedAmount = Tagged stackedGTs
, owner = signer
, lockedBy = []
}
governorOutputDatum :: GovernorDatum
governorOutputDatum = governorInputDatum {nextProposalId = getNextProposalId thisProposalId}
proposalLocks :: [ProposalLock]
proposalLocks =
[ ProposalLock (ResultTag 0) thisProposalId
, ProposalLock (ResultTag 1) thisProposalId
]
stakeOutputDatum :: StakeDatum
stakeOutputDatum = stakeInputDatum {lockedBy = proposalLocks}
validTimeRange = closedBoundedInterval 10 15
builder :: SpendingBuilder
builder =
mconcat
[ txId "1ffb9669335c908d9a4774a4bf7aa7bfafec91d015249b4138bc83fde4a3330a"
, fee $ Value.singleton "" "" 2
, timeRange $ closedBoundedInterval 10 15
, signedWith signer
, mint pst
, input $
script govValidatorHash
. withValue gst
. withDatum governorInputDatum
. withTxId "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865"
, input $
script stakeValidatorHash
. withValue (sst <> Value.assetClassValue (untag stake.gtClassRef) stackedGTs)
. withDatum stakeInputDatum
. withTxId "4262bbd0b3fc926b74eaa8abab5def6ce5e6b94f19cf221c02a16e7da8cd470f"
, output $
script proposalValidatorHash
. withValue (pst <> minAda)
. withDatum proposalDatum
, output $
script govValidatorHash
. withValue (gst <> minAda)
. withDatum governorOutputDatum
, output $
script stakeValidatorHash
. withValue (sst <> Value.assetClassValue (untag stake.gtClassRef) stackedGTs <> minAda)
. withDatum stakeOutputDatum
, withSpending $
script govValidatorHash
. withValue gst
. withDatum governorInputDatum
]
in buildSpendingUnsafe builder
{- This script context should be a valid transaction for minting authority for the effect scrips.
The following components will run:
- Governor validator
- Authority policy
- Proposal validator
There should be only one proposal the transaction.
The validity of the proposal will be checked:
- It's in 'Agora.Proposal.Locked' state.
- It has a 'winner' effect group, meaning that the votes meet the requirements.
The system will ensure that for every effect scrips in said effect group,
a newly minted GAT is sent to the corresponding effect, and properly tagged.
-}
mintGATs :: ScriptContext
mintGATs =
let pst = Value.singleton proposalPolicySymbol "" 1
gst = Value.assetClassValue govAssetClass 1
gat = Value.assetClassValue atAssetClass 1
mockEffect :: Validator
mockEffect = mkValidator $ noOpValidator ""
mockEffectHash :: ValidatorHash
mockEffectHash = validatorHash mockEffect
mockEffectOutputDatum :: Datum
mockEffectOutputDatum = unitDatum
atTokenName :: TokenName
atTokenName = TokenName hash
where
ValidatorHash hash = mockEffectHash
atAssetClass :: AssetClass
atAssetClass = AssetClass (authorityTokenSymbol, atTokenName)
governorInputDatum :: GovernorDatum
governorInputDatum =
GovernorDatum
{ proposalThresholds = def
, nextProposalId = ProposalId 5
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
}
effects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.singleton mockEffectHash $ toDatumHash mockEffectOutputDatum)
]
proposalVotes :: ProposalVotes
proposalVotes =
ProposalVotes $
AssocMap.fromList
[ (ResultTag 0, 100)
, (ResultTag 1, 2000) -- The winner
]
proposalInputDatum :: ProposalDatum
proposalInputDatum =
ProposalDatum
{ P.proposalId = ProposalId 0
, effects = effects
, status = Locked
, cosigners = [signer, signer2]
, thresholds = def
, votes = proposalVotes
, timingConfig = def
, startingTime = ProposalStartingTime 10
}
governorOutputDatum :: GovernorDatum
governorOutputDatum = governorInputDatum
proposalOutputDatum :: ProposalDatum
proposalOutputDatum = proposalInputDatum {status = Finished}
validTimeRange =
closedBoundedInterval
((def :: ProposalTimingConfig).lockingTime + 11)
((def :: ProposalTimingConfig).executingTime - 11)
builder :: SpendingBuilder
builder =
mconcat
[ txId "ff755f613c1f7487dfbf231325c67f481f7a97e9faf4d8b09ad41176fd65cbe7"
, signedWith signer
, signedWith signer2
, timeRange validTimeRange
, fee (Value.singleton "" "" 2)
, mint gat
, input $
script govValidatorHash
. withValue gst
. withDatum governorInputDatum
. withTxId "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865"
, input $
script proposalValidatorHash
. withValue pst
. withDatum proposalInputDatum
. withTxId "11b2162f267614b803761032b6333040fc61478ae788c088614ee9487ab0c1b7"
, output $
script govValidatorHash
. withValue (gst <> minAda)
. withDatum governorOutputDatum
, output $
script proposalValidatorHash
. withValue (pst <> minAda)
. withDatum proposalOutputDatum
, output $
script mockEffectHash
. withValue (gat <> minAda)
. withDatum mockEffectOutputDatum
, withSpending $
script govValidatorHash
. withValue gst
. withDatum governorInputDatum
]
in buildSpendingUnsafe builder
{- | A valid script context for changing the state datum of the governor.
In this case, the following components will run:
* Governor validator
* Effect script
The effect script should carry an valid tagged authority token,
and said token will be burnt in the transaction. We use 'noOpValidator'
here as a mock effect, so no actual change is done to the governor state.
TODO: use 'Agora.Effect.GovernorMutation.mutateGovernorEffect' as the mock effect in the future.
The governor will ensure the new governor state is valid.
-}
mutateState :: ScriptContext
mutateState =
let gst = Value.assetClassValue govAssetClass 1
gat = Value.assetClassValue atAssetClass 1
burntGAT = Value.assetClassValue atAssetClass (-1)
-- TODO: Use the *real* effect, see https://github.com/Liqwid-Labs/agora/pull/62
mockEffect :: Validator
mockEffect = mkValidator $ noOpValidator ""
mockEffectHash :: ValidatorHash
mockEffectHash = validatorHash mockEffect
atTokenName :: TokenName
atTokenName = TokenName hash
where
ValidatorHash hash = mockEffectHash
atAssetClass :: AssetClass
atAssetClass = AssetClass (authorityTokenSymbol, atTokenName)
mockEffectInputDatum :: Datum
mockEffectInputDatum = unitDatum
mockEffectOutputDatum :: Datum
mockEffectOutputDatum = mockEffectInputDatum
governorInputDatum :: GovernorDatum
governorInputDatum =
GovernorDatum
{ proposalThresholds = def
, nextProposalId = ProposalId 5
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
}
governorOutputDatum :: GovernorDatum
governorOutputDatum = governorInputDatum
builder :: SpendingBuilder
builder =
mconcat
[ txId "9a12a605086a9f866731869a42d0558036fc739c74fea3849aa41562c015aaf9"
, signedWith signer
, mint burntGAT
, fee $ Value.singleton "" "" 2
, input $
script govValidatorHash
. withValue gst
. withDatum governorInputDatum
. withTxId "f867238a04597c99a0b9858746557d305025cca3b9f78ea14d5c88c4cfcf58ff"
, input $
script mockEffectHash
. withValue gat
. withDatum mockEffectInputDatum
. withTxId "ecff06d7cf99089294569cc8b92609e44927278f9901730715d14634fbc10089"
, output $
script govValidatorHash
. withValue (gst <> minAda)
. withDatum governorOutputDatum
, input $
script mockEffectHash
. withValue minAda
. withDatum mockEffectOutputDatum
, withSpending $
script govValidatorHash
. withValue gst
. withDatum governorInputDatum
]
in buildSpendingUnsafe builder

View file

@ -0,0 +1,279 @@
{- |
Module : Sample.Governor.Initialize
Maintainer : connor@mlabs.city
Description: Generate sample data for testing the functionalities of minting GST.
Sample and utilities for testing the functionalities of minting GST.
-}
module Sample.Governor.Initialize (
mintGST,
Parameters (..),
totallyValidParameters,
invalidDatumTimingConfigParameters,
invalidDatumMaxTimeRangeWidthParameters,
invalidDatumThresholdsParameters,
withoutGovernorDatumParameters,
witnessNotPresentedParameters,
mintMoreThanOneGSTParameters,
mintGSTWithNoneEmptyNameParameters,
mkTestCase,
) where
import Agora.Bootstrap (agoraScripts)
import Agora.Governor (Governor (..), GovernorDatum (..))
import Agora.Proposal (ProposalId (..), ProposalThresholds (..))
import Agora.Proposal.Time (
MaxTimeRangeWidth (MaxTimeRangeWidth),
ProposalTimingConfig (ProposalTimingConfig),
)
import Agora.Scripts (
AgoraScripts (compiledGovernorPolicy),
governorSTAssetClass,
governorSTSymbol,
governorValidatorHash,
)
import Data.Default (Default (..))
import Plutarch.Context (
input,
mint,
output,
pubKey,
script,
signedWith,
txId,
withDatum,
withOutRef,
withValue,
)
import PlutusLedgerApi.V1 (
CurrencySymbol,
TxOutRef (TxOutRef),
ValidatorHash,
)
import PlutusLedgerApi.V1.Value (AssetClass (..))
import PlutusLedgerApi.V1.Value qualified as Value
import Sample.Shared (
minAda,
)
import Sample.Shared qualified as Shared
import Test.Specification (SpecificationTree, testPolicy)
import Test.Util (CombinableBuilder, mkMinting, pubKeyHashes, sortValue)
-- | The parameters that control the generation of the transaction.
data Parameters = Parameters
{ datumThresholdsValid :: Bool
-- ^ Whether the 'GovernorDatum.proposalThresholds' field of the output
-- governor datum is valid or not.
, datumMaxTimeRangeWidthValid :: Bool
-- ^ Whether the 'GovernorDatum.maximumProposalsPerStake'field of the
-- output governor datum is valid or not.
, datumTimingConfigValid :: Bool
-- ^ Whether the 'GovernorDatum.proposalTimings'field of the output
-- governor datum is valid or not.
, withGovernorDatum :: Bool
, -- Whether the output GST UTxO will carry the governor datum.
presentWitness :: Bool
, -- Whether to spend the UTxO referenced by 'Governor.gstOutRef'.
mintMoreThanOneStateToken :: Bool
, -- More than one GST will be minted if this is set to true.
mintStateTokenWithName :: Bool
-- The token name of the GST won't be empty if this is set to true.
}
--------------------------------------------------------------------------------
validGovernorOutputDatum :: GovernorDatum
validGovernorOutputDatum =
GovernorDatum
{ proposalThresholds = def
, nextProposalId = ProposalId 0
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
, maximumProposalsPerStake = 3
}
invalidProposalThresholds :: ProposalThresholds
invalidProposalThresholds = ProposalThresholds (-1) (-1) (-1)
invalidMaxTimeRangeWidth :: MaxTimeRangeWidth
invalidMaxTimeRangeWidth = MaxTimeRangeWidth 0
invalidProposalTimings :: ProposalTimingConfig
invalidProposalTimings = ProposalTimingConfig (-1) (-1) (-1) (-1)
witnessRef :: TxOutRef
witnessRef = TxOutRef "b0353c22b0bd6c5296a8eef160ba25d90b5dc82a9bb8bdaa6823ffc19515d6ad" 0
governor :: Governor
governor =
Shared.governor
{ gstOutRef = witnessRef
}
scripts :: AgoraScripts
scripts = agoraScripts Shared.deterministicTracingConfing governor
govAssetClass :: AssetClass
govAssetClass = governorSTAssetClass scripts
govValidatorHash :: ValidatorHash
govValidatorHash = governorValidatorHash scripts
govSymbol :: CurrencySymbol
govSymbol = governorSTSymbol scripts
--------------------------------------------------------------------------------
mintGST :: forall b. CombinableBuilder b => Parameters -> b
mintGST ps = builder
where
gstAC =
if ps.mintStateTokenWithName
then AssetClass (govSymbol, "12345")
else govAssetClass
gstCount =
if ps.mintMoreThanOneStateToken
then 10
else 1
gst = Value.assetClassValue gstAC gstCount
---
governorOutputDatum =
let th =
if ps.datumThresholdsValid
then def
else invalidProposalThresholds
trw =
if ps.datumMaxTimeRangeWidthValid
then def
else invalidMaxTimeRangeWidth
ptc =
if ps.datumTimingConfigValid
then def
else invalidProposalTimings
in validGovernorOutputDatum
{ proposalThresholds = th
, proposalTimings = ptc
, createProposalTimeRangeMaxWidth = trw
}
governorValue = sortValue $ gst <> minAda
---
witnessValue = minAda
witnessPubKey = head pubKeyHashes
---
witnessBuilder =
if ps.presentWitness
then
mconcat
[ input $
mconcat
[ pubKey witnessPubKey
, withValue witnessValue
, withOutRef witnessRef
]
, output $
mconcat
[ pubKey witnessPubKey
, withValue witnessValue
]
]
else mempty
---
govBuilder =
let datum =
if ps.withGovernorDatum
then withDatum governorOutputDatum
else mempty
in output $
mconcat
[ script govValidatorHash
, withValue governorValue
, datum
]
--
builder =
mconcat
[ txId "986b756ffb1c9839fc8d0b22a308ac91d5b5d0ebbfa683a47588c8a5cf70b5af"
, signedWith (pubKeyHashes !! 1)
, mint gst
, govBuilder
, witnessBuilder
]
--------------------------------------------------------------------------------
totallyValidParameters :: Parameters
totallyValidParameters =
Parameters
{ datumThresholdsValid = True
, datumMaxTimeRangeWidthValid = True
, datumTimingConfigValid = True
, withGovernorDatum = True
, presentWitness = True
, mintMoreThanOneStateToken = False
, mintStateTokenWithName = False
}
invalidDatumThresholdsParameters :: Parameters
invalidDatumThresholdsParameters =
totallyValidParameters
{ datumThresholdsValid = False
}
invalidDatumMaxTimeRangeWidthParameters :: Parameters
invalidDatumMaxTimeRangeWidthParameters =
totallyValidParameters
{ datumMaxTimeRangeWidthValid = False
}
invalidDatumTimingConfigParameters :: Parameters
invalidDatumTimingConfigParameters =
totallyValidParameters
{ datumTimingConfigValid = False
}
withoutGovernorDatumParameters :: Parameters
withoutGovernorDatumParameters =
totallyValidParameters
{ withGovernorDatum = False
}
witnessNotPresentedParameters :: Parameters
witnessNotPresentedParameters =
totallyValidParameters
{ presentWitness = False
}
mintMoreThanOneGSTParameters :: Parameters
mintMoreThanOneGSTParameters =
totallyValidParameters
{ mintMoreThanOneStateToken = True
}
mintGSTWithNoneEmptyNameParameters :: Parameters
mintGSTWithNoneEmptyNameParameters =
totallyValidParameters
{ mintStateTokenWithName = True
}
--------------------------------------------------------------------------------
{- | Create a test tree that runs the governor policy to test the initialization
of the governor.
-}
mkTestCase :: String -> Parameters -> Bool -> SpecificationTree
mkTestCase name ps valid =
testPolicy
valid
name
scripts.compiledGovernorPolicy
()
(mkMinting mintGST ps govSymbol)

View file

@ -0,0 +1,269 @@
module Sample.Governor.Mutate (
-- * Testing Utilities
GovernorOutputDatumValidity (..),
GATValidity (..),
GovernorParameters (..),
MockEffectParameters (..),
ParameterBundle (..),
-- * Testing Utilities
Validity (..),
mutate,
mkTestCase,
-- * Parameters Bundles
totallyValidBundle,
invalidBundles,
) where
import Agora.Effect.NoOp (noOpValidator)
import Agora.Governor (GovernorDatum (..), GovernorRedeemer (MutateGovernor))
import Agora.Proposal (ProposalId (ProposalId), ProposalThresholds (..))
import Agora.Scripts (AgoraScripts (..))
import Agora.Utils (validatorHashToTokenName)
import Data.Default (def)
import Plutarch.Api.V1 (PValidator, mkValidator, validatorHash)
import Plutarch.Context (
input,
mint,
output,
pubKey,
script,
withDatum,
withOutRef,
withValue,
)
import PlutusLedgerApi.V1 (
Data,
TxOutRef (TxOutRef),
ValidatorHash,
Value,
toData,
)
import PlutusLedgerApi.V1.Value qualified as Value
import Sample.Shared (
agoraScripts,
authorityTokenSymbol,
govAssetClass,
govValidatorHash,
minAda,
)
import Test.Specification (SpecificationTree, testValidator)
import Test.Util (CombinableBuilder, mkSpending, pubKeyHashes, sortValue, validatorHashes)
--------------------------------------------------------------------------------
-- | 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
, maximumProposalsPerStake = 3
}
mkGovernorOutputDatum ::
GovernorOutputDatumValidity ->
Maybe Data
mkGovernorOutputDatum DatumValid =
Just $
toData $
governorInputDatum
{ maximumProposalsPerStake = 4
}
mkGovernorOutputDatum ValueInvalid =
let invalidProposalThresholds =
ProposalThresholds
{ execute = -1
, create = -1
, vote = -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 = Value.assetClassValue govAssetClass 1
value = sortValue $ gst <> minAda
gstOutput =
if ps.stealGST
then pubKey $ head pubKeyHashes
else script govValidatorHash
withGSTDatum =
maybe mempty withDatum $
mkGovernorOutputDatum ps.governorOutputDatumValidity
in mconcat
[ input $
mconcat
[ script govValidatorHash
, withDatum governorInputDatum
, withValue value
, withOutRef governorRef
]
, output $
mconcat
[ gstOutput
, withGSTDatum
, withValue value
]
]
--------------------------------------------------------------------------------
mockEffectValidator :: ClosedTerm PValidator
mockEffectValidator = noOpValidator authorityTokenSymbol
mockEffectValidatorHash :: ValidatorHash
mockEffectValidatorHash = validatorHash $ mkValidator def mockEffectValidator
mkGATValue :: GATValidity -> Integer -> Value
mkGATValue NoGAT _ = mempty
mkGATValue v q =
let gatOwner = case v of
GATValid -> mockEffectValidatorHash
WrongTag -> head validatorHashes
in Value.singleton
authorityTokenSymbol
(validatorHashToTokenName gatOwner)
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 mockEffectValidatorHash
, withValue inputValue
]
, output $
mconcat
[ script mockEffectValidatorHash
, 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
agoraScripts.compiledGovernorValidator
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
}
}

View file

@ -1,815 +0,0 @@
{- |
Module : Sample.Proposal
Maintainer : emi@haskell.fyi
Description: Sample based testing for Proposal utxos
This module tests primarily the happy path for Proposal interactions
-}
module Sample.Proposal (
-- * Script contexts
proposalCreation,
cosignProposal,
proposalRef,
stakeRef,
voteOnProposal,
VotingParameters (..),
advanceProposalSuccess,
advanceProposalFailureTimeout,
TransitionParameters (..),
advanceFinishedProposal,
advanceProposalInsufficientVotes,
advanceProposalWithInvalidOutputStake,
) where
import Agora.Governor (GovernorDatum (..))
import Agora.Proposal (
Proposal (..),
ProposalDatum (..),
ProposalId (..),
ProposalStatus (..),
ProposalThresholds (..),
ProposalVotes (..),
ResultTag (..),
emptyVotesFor,
)
import Agora.Proposal.Time (
ProposalStartingTime (ProposalStartingTime),
ProposalTimingConfig (..),
)
import Agora.Stake (
ProposalLock (ProposalLock),
Stake (..),
StakeDatum (..),
)
import Data.Default.Class (Default (def))
import Data.Tagged (Tagged (..), untag)
import Plutarch.Context (
BaseBuilder,
MintingBuilder,
buildMintingUnsafe,
buildTxInfoUnsafe,
input,
mint,
output,
script,
signedWith,
timeRange,
txId,
withDatum,
withRefIndex,
withTxId,
withValue,
)
import PlutusLedgerApi.V1 (
Datum (Datum),
DatumHash,
POSIXTime,
POSIXTimeRange,
PubKeyHash,
ScriptContext (..),
ToData (toBuiltinData),
TxInInfo (TxInInfo),
TxInfo (..),
TxOut (TxOut, txOutAddress, txOutDatumHash, txOutValue),
TxOutRef (..),
ValidatorHash,
)
import PlutusLedgerApi.V1.Value qualified as Value (
assetClassValue,
singleton,
)
import PlutusTx.AssocMap qualified as AssocMap
import Sample.Proposal.Shared (proposalRef, stakeRef)
import Sample.Shared (
govValidatorHash,
minAda,
proposal,
proposalPolicySymbol,
proposalStartingTimeFromTimeRange,
proposalValidatorHash,
signer,
signer2,
stake,
stakeAddress,
stakeAssetClass,
stakeValidatorHash,
)
import Test.Util (
closedBoundedInterval,
datumPair,
toDatumHash,
updateMap,
)
proposalCreation :: ScriptContext
proposalCreation =
let st = Value.singleton proposalPolicySymbol "" 1 -- Proposal ST
effects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
]
proposalDatum :: ProposalDatum
proposalDatum =
ProposalDatum
{ proposalId = ProposalId 0
, effects = effects
, status = Draft
, cosigners = [signer]
, thresholds = def
, votes = emptyVotesFor effects
, timingConfig = def
, startingTime = proposalStartingTimeFromTimeRange validTimeRange
}
govBefore :: GovernorDatum
govBefore =
GovernorDatum
{ proposalThresholds = def
, nextProposalId = ProposalId 0
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
}
govAfter :: GovernorDatum
govAfter = govBefore {nextProposalId = ProposalId 1}
validTimeRange = closedBoundedInterval 10 15
builder :: MintingBuilder
builder =
mconcat
[ txId "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be"
, signedWith signer
, mint st
, input $
script govValidatorHash
. withValue (Value.assetClassValue proposal.governorSTAssetClass 1)
. withDatum govBefore
. withTxId "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be"
, output $
script proposalValidatorHash
. withValue (st <> Value.singleton "" "" 10_000_000)
. withDatum proposalDatum
, output $
script govValidatorHash
. withValue
( Value.assetClassValue proposal.governorSTAssetClass 1
<> Value.singleton "" "" 10_000_000
)
. withDatum govAfter
]
in buildMintingUnsafe builder
-- | This script context should be a valid transaction.
cosignProposal :: [PubKeyHash] -> TxInfo
cosignProposal newSigners =
let st = Value.singleton proposalPolicySymbol "" 1 -- Proposal ST
effects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
]
proposalBefore :: ProposalDatum
proposalBefore =
ProposalDatum
{ proposalId = ProposalId 0
, effects = effects
, status = Draft
, cosigners = [signer]
, thresholds = def
, votes = emptyVotesFor effects
, timingConfig = def
, startingTime = ProposalStartingTime 0
}
stakeDatum :: StakeDatum
stakeDatum = StakeDatum (Tagged 50_000_000) signer2 []
proposalAfter :: ProposalDatum
proposalAfter = proposalBefore {cosigners = newSigners <> proposalBefore.cosigners}
validTimeRange :: POSIXTimeRange
validTimeRange =
closedBoundedInterval
10
((def :: ProposalTimingConfig).draftTime - 10)
builder :: BaseBuilder
builder =
mconcat
[ txId "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be"
, mint st
, mconcat $ signedWith <$> newSigners
, timeRange validTimeRange
, input $
script proposalValidatorHash
. withValue (st <> Value.singleton "" "" 10_000_000)
. withDatum proposalBefore
. withTxId (txOutRefId proposalRef)
. withRefIndex (txOutRefIdx proposalRef)
, input $
script stakeValidatorHash
. withValue
( Value.singleton "" "" 10_000_000
<> Value.assetClassValue (untag stake.gtClassRef) 50_000_000
<> Value.assetClassValue stakeAssetClass 1
)
. withDatum stakeDatum
. withTxId (txOutRefId stakeRef)
. withRefIndex (txOutRefIdx stakeRef)
, output $
script proposalValidatorHash
. withValue (st <> Value.singleton "" "" 10_000_000)
. withDatum proposalAfter
, output $
script stakeValidatorHash
. withValue
( Value.singleton "" "" 10_000_000
<> Value.assetClassValue (untag stake.gtClassRef) 50_000_000
<> Value.assetClassValue stakeAssetClass 1
)
. withDatum stakeDatum
]
in buildTxInfoUnsafe builder
--------------------------------------------------------------------------------
-- | Parameters for creating a voting transaction.
data VotingParameters = VotingParameters
{ voteFor :: ResultTag
-- ^ The outcome the transaction is voting for.
, voteCount :: Integer
-- ^ The count of votes.
}
-- | Create a valid transaction that votes on a propsal, given the parameters.
voteOnProposal :: VotingParameters -> TxInfo
voteOnProposal params =
let pst = Value.singleton proposalPolicySymbol "" 1
sst = Value.assetClassValue stakeAssetClass 1
---
stakeOwner = signer
---
effects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
]
---
initialVotes :: AssocMap.Map ResultTag Integer
initialVotes =
AssocMap.fromList
[ (ResultTag 0, 42)
, (ResultTag 1, 4242)
]
---
proposalInputDatum :: ProposalDatum
proposalInputDatum =
ProposalDatum
{ proposalId = ProposalId 42
, effects = effects
, status = VotingReady
, cosigners = [stakeOwner]
, thresholds = def
, votes = ProposalVotes initialVotes
, timingConfig = def
, startingTime = ProposalStartingTime 0
}
---
existingLocks :: [ProposalLock]
existingLocks =
[ ProposalLock (ResultTag 0) (ProposalId 0)
, ProposalLock (ResultTag 2) (ProposalId 1)
]
---
stakeInputDatum :: StakeDatum
stakeInputDatum =
StakeDatum
{ stakedAmount = Tagged params.voteCount
, owner = stakeOwner
, lockedBy = existingLocks
}
---
updatedVotes :: AssocMap.Map ResultTag Integer
updatedVotes = updateMap (Just . (+ params.voteCount)) params.voteFor initialVotes
---
proposalOutputDatum :: ProposalDatum
proposalOutputDatum =
proposalInputDatum
{ votes = ProposalVotes updatedVotes
}
---
-- Off-chain code should do exactly like this: prepend new lock toStatus the list.
updatedLocks :: [ProposalLock]
updatedLocks = ProposalLock params.voteFor proposalInputDatum.proposalId : existingLocks
---
stakeOutputDatum :: StakeDatum
stakeOutputDatum =
stakeInputDatum
{ lockedBy = updatedLocks
}
---
validTimeRange =
closedBoundedInterval
((def :: ProposalTimingConfig).draftTime + 1)
((def :: ProposalTimingConfig).votingTime - 1)
builder :: BaseBuilder
builder =
mconcat
[ txId "827598fb2d69a896bbd9e645bb14c307df907f422b39eecbe4d6329bc30b428c"
, signedWith stakeOwner
, timeRange validTimeRange
, input $
script proposalValidatorHash
. withValue pst
. withDatum proposalInputDatum
. withTxId (txOutRefId proposalRef)
. withRefIndex (txOutRefIdx proposalRef)
, input $
script stakeValidatorHash
. withValue
( sst
<> Value.assetClassValue (untag stake.gtClassRef) params.voteCount
<> minAda
)
. withDatum stakeInputDatum
. withTxId (txOutRefId stakeRef)
. withRefIndex (txOutRefIdx stakeRef)
, output $
script proposalValidatorHash
. withValue pst
. withDatum proposalOutputDatum
, output $
script stakeValidatorHash
. withValue
( sst
<> Value.assetClassValue (untag stake.gtClassRef) params.voteCount
<> minAda
)
. withDatum stakeOutputDatum
]
in buildTxInfoUnsafe builder
--------------------------------------------------------------------------------
-- | Parameters for state transition of proposals.
data TransitionParameters = TransitionParameters
{ -- The initial status of the proposal.
initialProposalStatus :: ProposalStatus
, -- The starting time of the proposal.
proposalStartingTime :: ProposalStartingTime
}
-- | Create a 'TxInfo' that update the status of a proposal.
mkTransitionTxInfo ::
-- | Initial state of the proposal.
ProposalStatus ->
-- | Next state of the proposal.
ProposalStatus ->
-- | Effects.
AssocMap.Map ResultTag (AssocMap.Map ValidatorHash DatumHash) ->
-- | Votes.
ProposalVotes ->
-- | Starting time of the proposal.
ProposalStartingTime ->
-- | Valid time range of the transaction.
POSIXTimeRange ->
-- | Whether to add an unchanged stake or not.
Bool ->
TxInfo
mkTransitionTxInfo from to effects votes startingTime validTime shouldAddUnchangedStake =
let pst = Value.singleton proposalPolicySymbol "" 1
sst = Value.assetClassValue stakeAssetClass 1
proposalInputDatum :: ProposalDatum
proposalInputDatum =
ProposalDatum
{ proposalId = ProposalId 0
, effects = effects
, status = from
, cosigners = [signer]
, thresholds = def
, votes = votes
, timingConfig = def
, startingTime = startingTime
}
proposalOutputDatum :: ProposalDatum
proposalOutputDatum =
proposalInputDatum
{ status = to
}
stakeOwner = signer
stakedAmount = 200
existingLocks :: [ProposalLock]
existingLocks =
[ ProposalLock (ResultTag 0) (ProposalId 0)
, ProposalLock (ResultTag 2) (ProposalId 1)
]
stakeInputDatum :: StakeDatum
stakeInputDatum =
StakeDatum
{ stakedAmount = Tagged stakedAmount
, owner = stakeOwner
, lockedBy = existingLocks
}
stakeOutputDatum :: StakeDatum
stakeOutputDatum = stakeInputDatum
stakeBuilder :: BaseBuilder
stakeBuilder =
if shouldAddUnchangedStake
then
mconcat
[ input $
script stakeValidatorHash
. withValue sst
. withDatum stakeInputDatum
. withTxId (txOutRefId stakeRef)
, output $
script stakeValidatorHash
. withValue (sst <> minAda)
. withDatum stakeOutputDatum
]
else mempty
builder :: BaseBuilder
builder =
mconcat
[ txId "95ba4015e30aef16a3461ea97a779f814aeea6b8009d99a94add4b8293be737a"
, signedWith stakeOwner
, timeRange validTime
, input $
script proposalValidatorHash
. withValue pst
. withDatum proposalInputDatum
. withTxId (txOutRefId proposalRef)
, output $
script proposalValidatorHash
. withValue (pst <> minAda)
. withDatum proposalOutputDatum
]
in buildTxInfoUnsafe $ builder <> stakeBuilder
-- | Wrapper around 'advanceProposalSuccess'', with valid stake.
advanceProposalSuccess :: TransitionParameters -> TxInfo
advanceProposalSuccess ps = advanceProposalSuccess' ps True
{- | Create a valid 'TxInfo' that advances a proposal, given the parameters.
The second parameter determines wherther valid stake should be included.
Note that 'TransitionParameters.initialProposalStatus' should not be 'Finished'.
-}
advanceProposalSuccess' :: TransitionParameters -> Bool -> TxInfo
advanceProposalSuccess' params =
let -- Status of the output proposal.
toStatus :: ProposalStatus
toStatus = case params.initialProposalStatus of
Draft -> VotingReady
VotingReady -> Locked
Locked -> Finished
Finished -> error "Cannot advance 'Finished' proposal"
effects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
]
emptyVotes@(ProposalVotes emptyVotes') = emptyVotesFor effects
-- Set the vote count of outcome 0 to @def.countingVoting + 1@,
-- meaning that outcome 0 will be the winner.
outcome0WinningVotes =
ProposalVotes $
updateMap
(\_ -> Just $ untag (def :: ProposalThresholds).execute + 1)
(ResultTag 0)
emptyVotes'
votes :: ProposalVotes
votes = case params.initialProposalStatus of
Draft -> emptyVotes
-- With sufficient votes
_ -> outcome0WinningVotes
proposalStartingTime :: POSIXTime
proposalStartingTime =
let (ProposalStartingTime startingTime) = params.proposalStartingTime
in startingTime
timeRange :: POSIXTimeRange
timeRange = case params.initialProposalStatus of
-- [S + 1, S + D - 1]
Draft ->
closedBoundedInterval
(proposalStartingTime + 1)
(proposalStartingTime + (def :: ProposalTimingConfig).draftTime - 1)
-- [S + D + V + 1, S + D + V + L - 1]
VotingReady ->
closedBoundedInterval
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ 1
)
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ (def :: ProposalTimingConfig).lockingTime
- 1
)
-- [S + D + V + L + 1, S + + D + V + L + E - 1]
Locked ->
closedBoundedInterval
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ (def :: ProposalTimingConfig).lockingTime
+ 1
)
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ (def :: ProposalTimingConfig).lockingTime
+ (def :: ProposalTimingConfig).executingTime - 1
)
Finished -> error "Cannot advance 'Finished' proposal"
in mkTransitionTxInfo
params.initialProposalStatus
toStatus
effects
votes
params.proposalStartingTime
timeRange
{- | Create a valid 'TxInfo' that advances a proposal to failed state, given the parameters.
The reason why the proposal fails is the proposal has ran out of time.
Note that 'TransitionParameters.initialProposalStatus' should not be 'Finished'.
-}
advanceProposalFailureTimeout :: TransitionParameters -> TxInfo
advanceProposalFailureTimeout params =
let effects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
]
emptyVotes@(ProposalVotes emptyVotes') = emptyVotesFor effects
-- Set the vote count of outcome 0 to @def.countingVoting + 1@,
-- meaning that outcome 0 will be the winner.
outcome0WinningVotes =
ProposalVotes $
updateMap
(\_ -> Just $ untag (def :: ProposalThresholds).vote + 1)
(ResultTag 0)
emptyVotes'
votes :: ProposalVotes
votes = case params.initialProposalStatus of
Draft -> emptyVotes
-- With sufficient votes
_ -> outcome0WinningVotes
proposalStartingTime :: POSIXTime
proposalStartingTime =
let (ProposalStartingTime startingTime) = params.proposalStartingTime
in startingTime
timeRange :: POSIXTimeRange
timeRange = case params.initialProposalStatus of
-- [S + D + 1, S + D + V - 1]
Draft ->
closedBoundedInterval
(proposalStartingTime + (def :: ProposalTimingConfig).draftTime + 1)
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime - 1
)
-- [S + D + V + L + 1, S + D + V + L + E -1]
VotingReady ->
closedBoundedInterval
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ (def :: ProposalTimingConfig).lockingTime
+ 1
)
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ (def :: ProposalTimingConfig).lockingTime
+ (def :: ProposalTimingConfig).executingTime
- 1
)
-- [S + D + V + L + E + 1, S + D + V + L + E + 100]
Locked ->
closedBoundedInterval
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ (def :: ProposalTimingConfig).lockingTime
+ (def :: ProposalTimingConfig).executingTime
+ 1
)
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ (def :: ProposalTimingConfig).lockingTime
+ (def :: ProposalTimingConfig).executingTime
+ 100
)
Finished -> error "Cannot advance 'Finished' proposal"
in mkTransitionTxInfo
params.initialProposalStatus
Finished
effects
votes
params.proposalStartingTime
timeRange
True
-- | An invalid 'TxInfo' that tries to advance a 'VotingReady' proposal without sufficient votes.
advanceProposalInsufficientVotes :: TxInfo
advanceProposalInsufficientVotes =
let effects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
]
-- Insufficient votes.
votes =
ProposalVotes
( AssocMap.fromList
[ (ResultTag 0, 1)
, (ResultTag 1, 0)
]
)
proposalStartingTime = 0
-- Valid time range.
-- [S + D + 1, S + V + 10]
timeRange =
closedBoundedInterval
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ 1
)
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ 10
)
in mkTransitionTxInfo
VotingReady
Locked
effects
votes
(ProposalStartingTime proposalStartingTime)
timeRange
True
-- | An invalid 'TxInfo' that tries to advance a 'Finished' proposal.
advanceFinishedProposal :: TxInfo
advanceFinishedProposal =
let effects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
]
-- Set the vote count of outcome 0 to @def.countingVoting + 1@,
-- meaning that outcome 0 will be the winner.
outcome0WinningVotes =
ProposalVotes $
AssocMap.fromList
[ (ResultTag 0, untag (def :: ProposalThresholds).vote + 1)
, (ResultTag 1, 0)
]
---
timeRange =
closedBoundedInterval
((def :: ProposalTimingConfig).lockingTime + 1)
((def :: ProposalTimingConfig).executingTime - 1)
in mkTransitionTxInfo
Finished
Finished
effects
outcome0WinningVotes
(ProposalStartingTime 0)
timeRange
True
{- | An illegal 'TxInfo' that tries to output a changed stake with 'AdvanceProposal'.
From the perspective of stake validator, the transition is totally valid,
so the proposal validator should reject this.
-}
advanceProposalWithInvalidOutputStake :: TxInfo
advanceProposalWithInvalidOutputStake =
let templateTxInfo =
advanceProposalSuccess'
TransitionParameters
{ initialProposalStatus = VotingReady
, proposalStartingTime = ProposalStartingTime 0
}
False
---
-- Now we create a new lock on an arbitrary stake
sst = Value.assetClassValue stakeAssetClass 1
---
stakeOwner = signer
stakedAmount = 200
---
existingLocks :: [ProposalLock]
existingLocks =
[ ProposalLock (ResultTag 0) (ProposalId 0)
, ProposalLock (ResultTag 2) (ProposalId 1)
]
---
stakeInputDatum' :: StakeDatum
stakeInputDatum' =
StakeDatum
{ stakedAmount = Tagged stakedAmount
, owner = stakeOwner
, lockedBy = existingLocks
}
stakeInputDatum :: Datum
stakeInputDatum = Datum $ toBuiltinData stakeInputDatum'
stakeInput :: TxOut
stakeInput =
TxOut
{ txOutAddress = stakeAddress
, txOutValue =
mconcat
[ sst
, Value.assetClassValue (untag stake.gtClassRef) stakedAmount
, minAda
]
, txOutDatumHash = Just $ toDatumHash stakeInputDatum
}
---
updatedLocks :: [ProposalLock]
updatedLocks = ProposalLock (ResultTag 42) (ProposalId 27) : existingLocks
---
stakeOutputDatum' :: StakeDatum
stakeOutputDatum' =
stakeInputDatum'
{ lockedBy = updatedLocks
}
stakeOutputDatum :: Datum
stakeOutputDatum = Datum $ toBuiltinData stakeOutputDatum'
stakeOutput :: TxOut
stakeOutput =
stakeInput
{ txOutDatumHash = Just $ toDatumHash stakeOutputDatum
}
in templateTxInfo
{ txInfoInputs = TxInInfo stakeRef stakeInput : templateTxInfo.txInfoInputs
, txInfoOutputs = stakeOutput : templateTxInfo.txInfoOutputs
, txInfoData =
(datumPair <$> [stakeInputDatum, stakeOutputDatum])
<> templateTxInfo.txInfoData
, txInfoSignatories = [stakeOwner]
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,339 @@
{- |
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 (
Parameters (..),
validCosignNParameters,
duplicateCosignersParameters,
statusNotDraftCosignNParameters,
invalidStakeOutputParameters,
mkTestTree,
) where
import Agora.Governor (Governor (..))
import Agora.Proposal (
ProposalDatum (..),
ProposalId (ProposalId),
ProposalRedeemer (Cosign),
ProposalStatus (..),
ResultTag (ResultTag),
emptyVotesFor,
)
import Agora.Proposal.Time (
ProposalStartingTime (ProposalStartingTime),
ProposalTimingConfig (draftTime),
)
import Agora.SafeMoney (GTTag)
import Agora.Scripts (AgoraScripts (..))
import Agora.Stake (
StakeDatum (StakeDatum, owner),
StakeRedeemer (WitnessStake),
stakedAmount,
)
import Data.Coerce (coerce)
import Data.Default (def)
import Data.List (sort)
import Data.Tagged (Tagged, untag)
import Plutarch.Context (
input,
output,
script,
signedWith,
timeRange,
txId,
withDatum,
withOutRef,
withTxId,
withValue,
)
import PlutusLedgerApi.V1 (
POSIXTimeRange,
PubKeyHash,
TxOutRef (..),
Value,
)
import PlutusLedgerApi.V1.Value qualified as Value
import PlutusTx.AssocMap qualified as AssocMap
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef)
import Sample.Shared (
agoraScripts,
governor,
minAda,
proposalPolicySymbol,
proposalValidatorHash,
signer,
stakeAssetClass,
stakeValidatorHash,
)
import Test.Specification (
SpecificationTree,
group,
testValidator,
)
import Test.Util (CombinableBuilder, closedBoundedInterval, mkSpending, pubKeyHashes, sortValue)
-- | Parameters for cosigning a proposal.
data Parameters = Parameters
{ newCosigners :: [PubKeyHash]
-- ^ New cosigners to be added, and the owners of the generated stakes.
, proposalStatus :: ProposalStatus
-- ^ Current state of the proposal.
, alterOutputStakes :: Bool
-- ^ Whether to generate invalid stake outputs.
-- In particular, the 'stakedAmount' of all the stake datums will be set to zero.
}
-- | Owner of the creator stake, doesn't really matter in this case.
proposalCreator :: PubKeyHash
proposalCreator = signer
-- | The amount of GTs every generated stake has, doesn't really matter in this case.
perStakedGTs :: Tagged GTTag Integer
perStakedGTs = 5
{- | Create input proposal datum given the parameters.
In particular, 'status' is set to 'proposalStstus'.
-}
mkProposalInputDatum :: Parameters -> ProposalDatum
mkProposalInputDatum ps =
let effects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
]
in ProposalDatum
{ proposalId = ProposalId 0
, effects = effects
, status = ps.proposalStatus
, cosigners = [proposalCreator]
, thresholds = def
, votes = emptyVotesFor effects
, timingConfig = def
, startingTime = ProposalStartingTime 0
}
{- | Create the output proposal datum given the parameters.
The 'newCosigners' is added to the exisiting list of cosigners, note the said list should be sorted in
ascending order.
-}
mkProposalOutputDatum :: Parameters -> ProposalDatum
mkProposalOutputDatum ps =
let inputDatum = mkProposalInputDatum ps
in inputDatum
{ cosigners = sort $ inputDatum.cosigners <> ps.newCosigners
}
-- | Create all the input stakes given the parameters.
mkStakeInputDatums :: Parameters -> [StakeDatum]
mkStakeInputDatums = fmap (\pk -> StakeDatum perStakedGTs pk Nothing []) . newCosigners
-- | Create a 'TxInfo' that tries to cosign a proposal with new cosigners.
cosign :: forall b. CombinableBuilder b => Parameters -> b
cosign ps = builder
where
pst = Value.singleton proposalPolicySymbol "" 1
sst = Value.assetClassValue stakeAssetClass 1
---
stakeInputDatums :: [StakeDatum]
stakeInputDatums = mkStakeInputDatums ps
stakeValue :: Value
stakeValue =
sortValue $
minAda
<> Value.assetClassValue
(untag governor.gtClassRef)
(untag perStakedGTs)
<> sst
stakeBuilder =
foldMap
( \(stakeDatum, refIdx) ->
let stakeOutputDatum =
if ps.alterOutputStakes
then stakeDatum {stakedAmount = 0}
else stakeDatum
in mconcat
[ input $
mconcat
[ script stakeValidatorHash
, withValue stakeValue
, withDatum stakeDatum
, withTxId stakeTxRef
, withOutRef (mkStakeRef refIdx)
]
, output $
mconcat
[ script stakeValidatorHash
, withValue stakeValue
, withDatum stakeOutputDatum
]
, signedWith stakeDatum.owner
]
)
$ zip
stakeInputDatums
[0 ..]
---
proposalInputDatum :: ProposalDatum
proposalInputDatum = mkProposalInputDatum ps
proposalOutputDatum :: ProposalDatum
proposalOutputDatum = mkProposalOutputDatum ps
proposalBuilder =
mconcat
[ input $
mconcat
[ script proposalValidatorHash
, withValue pst
, withDatum proposalInputDatum
, withTxId proposalTxRef
, withOutRef proposalRef
]
, output $
mconcat
[ script proposalValidatorHash
, withValue (sortValue (pst <> minAda))
, withDatum proposalOutputDatum
]
]
validTimeRange :: POSIXTimeRange
validTimeRange =
closedBoundedInterval
(coerce proposalInputDatum.startingTime + 1)
( coerce proposalInputDatum.startingTime
+ proposalInputDatum.timingConfig.draftTime - 1
)
---
builder =
mconcat
[ txId "05c67819fc3381a2052b929ab439244b7b5fe3b3bd07f2134055bbbb21bd9e52"
, timeRange validTimeRange
, proposalBuilder
, stakeBuilder
]
-- | Reference index of the proposal UTXO.
proposalRefIdx :: Integer
proposalRefIdx = 1
-- | Spend the proposal ST.
proposalRef :: TxOutRef
proposalRef = TxOutRef proposalTxRef proposalRefIdx
-- | Consume the given stake.
mkStakeRef :: Int -> TxOutRef
mkStakeRef idx =
TxOutRef
stakeTxRef
$ proposalRefIdx + 1 + fromIntegral idx
-- | Create a proposal redeemer which cosigns with the new cosginers.
mkProposalRedeemer :: Parameters -> ProposalRedeemer
mkProposalRedeemer (sort . newCosigners -> cs) = Cosign cs
-- | Stake redeemer for cosuming all the stakes generated in the module.
stakeRedeemer :: StakeRedeemer
stakeRedeemer = WitnessStake
---
-- | Create a valid parameters that cosign the proposal with a given number of cosigners.
validCosignNParameters :: Int -> Parameters
validCosignNParameters n
| n > 0 =
Parameters
{ newCosigners = take n pubKeyHashes
, proposalStatus = Draft
, alterOutputStakes = False
}
| otherwise = error "Number of cosigners should be positive"
---
{- | Parameters that make 'cosign' yield duplicate cosigners.
Invalid for the ptoposal validator, perfectly valid for stake validator.
-}
duplicateCosignersParameters :: Parameters
duplicateCosignersParameters =
Parameters
{ newCosigners = [proposalCreator]
, proposalStatus = Draft
, alterOutputStakes = False
}
---
{- | Generate a list of parameters that sets proposal status to something other than 'Draft'.
Invalid for the ptoposal validator, perfectly valid for stake validator.
-}
statusNotDraftCosignNParameters :: Int -> [Parameters]
statusNotDraftCosignNParameters n =
map
( \st ->
Parameters
{ newCosigners = take n pubKeyHashes
, proposalStatus = st
, alterOutputStakes = False
}
)
[VotingReady, Locked, Finished]
---
{- | Parameters thet change the output stake datums.
Invalid for both proposal validator and stake validator.
-}
invalidStakeOutputParameters :: Parameters
invalidStakeOutputParameters =
(validCosignNParameters 2)
{ alterOutputStakes = True
}
---
-- | Create a test tree given the parameters. Both the proposal validator and stake validator will be run.
mkTestTree ::
-- | The name of the test group.
String ->
Parameters ->
-- | Are the parameters valid for the proposal validator?
Bool ->
SpecificationTree
mkTestTree name ps isValid = group name [proposal, stake]
where
spend = mkSpending cosign ps
proposal =
let proposalInputDatum = mkProposalInputDatum ps
in testValidator
isValid
"proposal"
agoraScripts.compiledProposalValidator
proposalInputDatum
(mkProposalRedeemer ps)
(spend proposalRef)
stake =
let idx = 0
stakeInputDatum = mkStakeInputDatums ps !! idx
isValid = not ps.alterOutputStakes
in testValidator
isValid
"stake"
agoraScripts.compiledStakeValidator
stakeInputDatum
stakeRedeemer
(spend $ mkStakeRef idx)

View file

@ -0,0 +1,458 @@
{- |
Module : Sample.Proposal.Create
Maintainer : connor@mlabs.city
Description: Generate sample data for testing the functionalities of creating proposals
Sample and utilities for testing the functionalities of creating proposals.
-}
module Sample.Proposal.Create (
Parameters (..),
mkTestTree,
totallyValidParameters,
invalidOutputGovernorDatumParameters,
useStakeOwnBySomeoneElseParameters,
invalidOutputStakeParameters,
addInvalidLocksParameters,
exceedMaximumProposalsParameters,
timeRangeNotTightParameters,
timeRangeNotClosedParameters,
invalidProposalStatusParameters,
) where
import Agora.Governor (
Governor (..),
GovernorDatum (..),
GovernorRedeemer (CreateProposal),
)
import Agora.Proposal (
ProposalDatum (..),
ProposalId (ProposalId),
ProposalStatus (..),
ResultTag (ResultTag),
emptyVotesFor,
)
import Agora.Proposal.Time (MaxTimeRangeWidth (MaxTimeRangeWidth), ProposalStartingTime (..))
import Agora.Scripts (AgoraScripts (..))
import Agora.Stake (
ProposalLock (..),
StakeDatum (..),
StakeRedeemer (PermitVote),
)
import Data.Coerce (coerce)
import Data.Default (Default (def))
import Data.Tagged (Tagged, untag)
import Plutarch.Context (
input,
mint,
output,
script,
signedWith,
timeRange,
txId,
withDatum,
withOutRef,
withValue,
)
import PlutusLedgerApi.V1 (
DatumHash,
POSIXTime (POSIXTime),
POSIXTimeRange,
PubKeyHash,
TxOutRef (TxOutRef),
ValidatorHash,
always,
)
import PlutusLedgerApi.V1.Value qualified as Value
import PlutusTx.AssocMap qualified as AssocMap
import Sample.Proposal.Shared (stakeTxRef)
import Sample.Shared (
agoraScripts,
govAssetClass,
govValidatorHash,
governor,
minAda,
proposalPolicySymbol,
proposalStartingTimeFromTimeRange,
proposalValidatorHash,
signer,
signer2,
stakeAssetClass,
stakeValidatorHash,
)
import Test.Specification (SpecificationTree, group, testPolicy, testValidator)
import Test.Util (CombinableBuilder, closedBoundedInterval, mkMinting, mkSpending, sortValue)
-- | Parameters for creating a proposal.
data Parameters = Parameters
{ advanceNextProposalId :: Bool
-- ^ Whether to advance 'GovernorDatum.nextProposalId'.
, createdMoreThanMaximumProposals :: Bool
-- ^ Try creating more than maximum amount of proposals.
, stakeOwnerSignsTheTransaction :: Bool
-- ^ Should the stake owner sign the transaction?
, invalidNewLocks :: Bool
-- ^ Place invalid new locks on the output stake.
, alterOutputStakeOwner :: Bool
-- ^ Whether to change the 'owner' field of the output stake datum.
, timeRangeTightEnough :: Bool
-- ^ Is 'TxInfo.validTimeRange' tight enough?
, timeRangeClosed :: Bool
-- ^ Is 'TxInfo.validTimeRange' closed?
, proposalStatus :: ProposalStatus
-- ^ The status of the newly created proposal.
}
--------------------------------------------------------------------------------
-- | See 'GovernorDatum.maximumProposalsPerStake'.
maxProposalPerStake :: Integer
maxProposalPerStake = 3
-- | The id of the proposal we are creating.
thisProposalId :: ProposalId
thisProposalId = ProposalId 25
-- | The arbitrary staked amount. Doesn;t really matter in this case.
stakedGTs :: Tagged _ Integer
stakedGTs = 5
-- | The owner of the stake.
stakeOwner :: PubKeyHash
stakeOwner = signer
{- | The invalid stake owner. If the 'alterOutputStakeOwner' is set to true,
the output stake owner will be set to this.
-}
alteredStakeOwner :: PubKeyHash
alteredStakeOwner = signer2
-- | Locks the stake that the input stake already has.
defLocks :: [ProposalLock]
defLocks = [Created (ProposalId 0)]
-- | The effect of the newly created proposal.
defEffects :: AssocMap.Map ResultTag (AssocMap.Map ValidatorHash DatumHash)
defEffects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
]
--------------------------------------------------------------------------------
-- | The governor input datum.
governorInputDatum :: GovernorDatum
governorInputDatum =
GovernorDatum
{ proposalThresholds = def
, nextProposalId = thisProposalId
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
, maximumProposalsPerStake = maxProposalPerStake
}
-- | Create governor output datum given the parameters.
mkGovernorOutputDatum :: Parameters -> GovernorDatum
mkGovernorOutputDatum ps =
let nextPid =
if ps.advanceNextProposalId
then ProposalId $ coerce thisProposalId + 1
else thisProposalId
in GovernorDatum
{ proposalThresholds = def
, nextProposalId = nextPid
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
, maximumProposalsPerStake = maxProposalPerStake
}
--------------------------------------------------------------------------------
-- | Create the stake input datum given the parameters.
mkStakeInputDatum :: Parameters -> StakeDatum
mkStakeInputDatum ps =
let locks =
if ps.createdMoreThanMaximumProposals
then
Created . ProposalId
<$> take
(fromInteger maxProposalPerStake)
[1 ..]
else defLocks
in StakeDatum
{ stakedAmount = stakedGTs
, owner = stakeOwner
, delegatedTo = Nothing
, lockedBy = locks
}
-- | Create the stake output datum given the parameters.
mkStakeOutputDatum :: Parameters -> StakeDatum
mkStakeOutputDatum ps =
let inputDatum = mkStakeInputDatum ps
newLocks =
if ps.invalidNewLocks
then
[ Voted thisProposalId (ResultTag 0)
, Voted thisProposalId (ResultTag 1)
]
else [Created thisProposalId]
locks = newLocks <> inputDatum.lockedBy
newOwner = mkOwner ps
in inputDatum
{ owner = newOwner
, lockedBy = locks
}
--------------------------------------------------------------------------------
{- | Create the proposal datum for the newly created proposal, given the
parameters.
-}
mkProposalOutputDatum :: Parameters -> ProposalDatum
mkProposalOutputDatum ps =
ProposalDatum
{ proposalId = thisProposalId
, effects = defEffects
, status = ps.proposalStatus
, cosigners = [mkOwner ps]
, thresholds = def
, votes = emptyVotesFor defEffects
, timingConfig = def
, startingTime = mkProposalStartingTime ps
}
--------------------------------------------------------------------------------
-- | Create time range for 'TxInfo.validTimeRange'.
mkTimeRange :: Parameters -> POSIXTimeRange
mkTimeRange ps =
if ps.timeRangeClosed
then
let s = 0
di :: POSIXTime = coerce (def @MaxTimeRangeWidth)
o = if ps.timeRangeTightEnough then (-1) else 1
in closedBoundedInterval s $ o + di
else always
-- | Get the starting time of the proposal.
mkProposalStartingTime :: Parameters -> ProposalStartingTime
mkProposalStartingTime ps =
if ps.timeRangeClosed
then proposalStartingTimeFromTimeRange $ mkTimeRange ps
else ProposalStartingTime 0
-- | Who should be the 'owner' of the output stake.
mkOwner :: Parameters -> PubKeyHash
mkOwner ps =
if ps.alterOutputStakeOwner
then alteredStakeOwner
else stakeOwner
--------------------------------------------------------------------------------
-- | Reference to the input stake UTXO.
stakeRef :: TxOutRef
stakeRef = TxOutRef stakeTxRef 1
-- | Reference to the input governor UTXO.
governorRef :: TxOutRef
governorRef = TxOutRef "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be" 3
--------------------------------------------------------------------------------
-- | Create a 'TxInfo' that spends a stake to create a new proposal.
createProposal :: forall b. CombinableBuilder b => Parameters -> b
createProposal ps = builder
where
pst = Value.singleton proposalPolicySymbol "" 1
sst = Value.assetClassValue stakeAssetClass 1
gst = Value.assetClassValue govAssetClass 1
---
governorValue = sortValue $ gst <> minAda
stakeValue =
sortValue $
sortValue $
sst
<> Value.assetClassValue (untag governor.gtClassRef) (untag stakedGTs)
<> minAda
proposalValue = sortValue $ pst <> minAda
---
withSig =
if ps.stakeOwnerSignsTheTransaction
then signedWith stakeOwner
else mempty
---
builder =
mconcat
[ txId "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be"
, ---
withSig
, ---
mint pst
, ---
timeRange $ mkTimeRange ps
, input $
mconcat
[ script govValidatorHash
, withValue governorValue
, withDatum governorInputDatum
, withOutRef governorRef
]
, output $
mconcat
[ script govValidatorHash
, withValue governorValue
, withDatum (mkGovernorOutputDatum ps)
]
, ---
input $
mconcat
[ script stakeValidatorHash
, withValue stakeValue
, withDatum (mkStakeInputDatum ps)
, withOutRef stakeRef
]
, output $
mconcat
[ script stakeValidatorHash
, withValue stakeValue
, withDatum (mkStakeOutputDatum ps)
]
, ---
output $
mconcat
[ script proposalValidatorHash
, withValue proposalValue
, withDatum (mkProposalOutputDatum ps)
]
]
--------------------------------------------------------------------------------
-- | Spend the stake with the 'PermitVote' redeemer.
stakeRedeemer :: StakeRedeemer
stakeRedeemer = PermitVote
-- | Spend the governor with the 'CreateProposal' redeemer.
governorRedeemer :: GovernorRedeemer
governorRedeemer = CreateProposal
-- | Mint the PST with an arbitrary redeemer. Doesn't really matter.
proposalPolicyRedeemer :: ()
proposalPolicyRedeemer = ()
--------------------------------------------------------------------------------
totallyValidParameters :: Parameters
totallyValidParameters =
Parameters
{ advanceNextProposalId = True
, createdMoreThanMaximumProposals = False
, stakeOwnerSignsTheTransaction = True
, invalidNewLocks = False
, alterOutputStakeOwner = False
, timeRangeTightEnough = True
, timeRangeClosed = True
, proposalStatus = Draft
}
invalidOutputGovernorDatumParameters :: Parameters
invalidOutputGovernorDatumParameters =
totallyValidParameters
{ advanceNextProposalId = False
}
useStakeOwnBySomeoneElseParameters :: Parameters
useStakeOwnBySomeoneElseParameters =
totallyValidParameters
{ stakeOwnerSignsTheTransaction = False
}
invalidOutputStakeParameters :: Parameters
invalidOutputStakeParameters =
totallyValidParameters
{ alterOutputStakeOwner = True
}
addInvalidLocksParameters :: Parameters
addInvalidLocksParameters =
totallyValidParameters
{ invalidNewLocks = True
}
exceedMaximumProposalsParameters :: Parameters
exceedMaximumProposalsParameters =
totallyValidParameters
{ createdMoreThanMaximumProposals = True
}
timeRangeNotTightParameters :: Parameters
timeRangeNotTightParameters =
totallyValidParameters
{ timeRangeTightEnough = False
}
timeRangeNotClosedParameters :: Parameters
timeRangeNotClosedParameters =
totallyValidParameters
{ timeRangeClosed = False
}
invalidProposalStatusParameters :: [Parameters]
invalidProposalStatusParameters =
map
( \st ->
totallyValidParameters {proposalStatus = st}
)
[VotingReady, Locked, Finished]
--------------------------------------------------------------------------------
{- | Create a test tree that runs the proposal minting policy, the governor
validator and the stake validator to test the functionalities of creting
proposals
-}
mkTestTree :: String -> Parameters -> Bool -> Bool -> Bool -> SpecificationTree
mkTestTree
name
ps
validForProposalPolicy
validForGovernorValidator
validForStakeValidator =
group name [proposalTest, governorTest, stakeTest]
where
mint = mkMinting createProposal ps
spend = mkSpending createProposal ps
proposalTest =
testPolicy
validForProposalPolicy
"proposal"
agoraScripts.compiledProposalPolicy
proposalPolicyRedeemer
(mint proposalPolicySymbol)
governorTest =
testValidator
validForGovernorValidator
"governor"
agoraScripts.compiledGovernorValidator
governorInputDatum
governorRedeemer
(spend governorRef)
stakeTest =
testValidator
validForStakeValidator
"stake"
agoraScripts.compiledStakeValidator
(mkStakeInputDatum ps)
stakeRedeemer
(spend stakeRef)

View file

@ -1,9 +1,21 @@
module Sample.Proposal.Shared (proposalRef, stakeRef) where {- |
Module : Sample.Proposal.Shared
Maintainer : connor@mlabs.city
Description: Shared constants for proposal samples
import PlutusLedgerApi.V1 (TxOutRef (..)) Shared constants for proposal samples.
-}
module Sample.Proposal.Shared (proposalTxRef, stakeTxRef, governorTxRef) where
proposalRef :: TxOutRef import PlutusLedgerApi.V1 (TxId)
proposalRef = TxOutRef "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be" 1
stakeRef :: TxOutRef -- | 'TxId' of all the proposal inputs in the samples.
stakeRef = TxOutRef "0ca36f3a357bc69579ab2531aecd1e7d3714d993c7820f40b864be15" 0 proposalTxRef :: TxId
proposalTxRef = "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be"
-- | 'TxId' of all the stake inputs in the samples.
stakeTxRef :: TxId
stakeTxRef = "0ca36f3a357bc69579ab2531aecd1e7d3714d993c7820f40b864be15"
governorTxRef :: TxId
governorTxRef = "cb076140e80d240f9c89e478aedbddbe6f4734fecbd0ae3e37404c12e7798c0f"

View file

@ -1,29 +1,31 @@
{- |
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.UnlockStake ( module Sample.Proposal.UnlockStake (
unlockStake,
StakeRole (..), StakeRole (..),
UnlockStakeParameters (..), Parameters (..),
votesTemplate, unlockStake,
emptyEffectFor, mkTestTree,
mkProposalInputDatum, mkVoterRetractVotesWhileVotingParameters,
mkStakeInputDatum, mkVoterCreatorRetractVotesWhileVotingParameters,
mkProposalValidatorTestCase, mkCreatorRemoveCreatorLocksWhenFinishedParameters,
mkVoterCreatorRemoveAllLocksWhenFinishedParameters,
mkVoterUnlockStakeAfterVotingParameters,
mkVoterCreatorRemoveVoteLocksWhenLockedParameters,
mkRetractVotesWhileNotVoting,
mkUnockIrrelevantStakeParameters,
mkRemoveCreatorLockBeforeFinishedParameters,
mkRetractVotesWithCreatorStakeParamaters,
mkAlterStakeParameters,
) where ) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import PlutusLedgerApi.V1 ( import Agora.Governor (Governor (..))
DatumHash,
ScriptContext (..),
ScriptPurpose (Spending),
TxInfo (..),
TxOutRef (..),
ValidatorHash,
)
import PlutusLedgerApi.V1.Value qualified as Value
import PlutusTx.AssocMap qualified as AssocMap
--------------------------------------------------------------------------------
import Agora.Proposal ( import Agora.Proposal (
ProposalDatum (..), ProposalDatum (..),
ProposalId (..), ProposalId (..),
@ -33,29 +35,41 @@ import Agora.Proposal (
ResultTag (..), ResultTag (..),
) )
import Agora.Proposal.Time (ProposalStartingTime (ProposalStartingTime)) import Agora.Proposal.Time (ProposalStartingTime (ProposalStartingTime))
import Agora.Stake (ProposalLock (ProposalLock), Stake (..), StakeDatum (..)) import Agora.Scripts (AgoraScripts (..))
import Agora.Stake (ProposalLock (..), StakeDatum (..), StakeRedeemer (RetractVotes))
import Data.Default.Class (Default (def))
import Data.Tagged (Tagged (..), untag)
import Plutarch.Context (
input,
output,
script,
signedWith,
txId,
withDatum,
withOutRef,
withValue,
)
import PlutusLedgerApi.V1 (
DatumHash,
PubKeyHash,
TxOutRef (..),
ValidatorHash,
)
import PlutusLedgerApi.V1.Value qualified as Value
import PlutusTx.AssocMap qualified as AssocMap
import Sample.Proposal.Shared (stakeTxRef)
import Sample.Shared ( import Sample.Shared (
agoraScripts,
governor,
minAda, minAda,
proposalPolicySymbol, proposalPolicySymbol,
proposalValidatorHash, proposalValidatorHash,
signer, signer,
stake,
stakeAssetClass, stakeAssetClass,
stakeValidatorHash, stakeValidatorHash,
) )
import Test.Util (sortValue, updateMap) import Test.Specification (SpecificationTree, group, testValidator)
import Test.Util (CombinableBuilder, mkSpending, sortValue, updateMap)
--------------------------------------------------------------------------------
import Agora.Proposal.Scripts (proposalValidator)
import Control.Monad (join)
import Data.Coerce (coerce)
import Data.Default.Class (Default (def))
import Data.Tagged (Tagged (..), untag)
import Plutarch.Context (BaseBuilder, buildTxInfoUnsafe, input, output, script, txId, withDatum, withRefIndex, withTxId, withValue)
import Sample.Proposal.Shared (proposalRef, stakeRef)
import Sample.Shared qualified as Shared
import Test.Specification (SpecificationTree, validatorFailsWith, validatorSucceedsWith)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -77,106 +91,131 @@ emptyEffectFor (ProposalVotes vs) =
map (,AssocMap.empty) (AssocMap.keys vs) map (,AssocMap.empty) (AssocMap.keys vs)
-- | The default vote option that will be used by functions in this module. -- | The default vote option that will be used by functions in this module.
defaultVoteFor :: ResultTag defVoteFor :: ResultTag
defaultVoteFor = ResultTag 0 defVoteFor = ResultTag 0
-- | The default number of GTs the stake will have. -- | The default number of GTs the stake will have.
defaultStakedGTs :: Tagged _ Integer defStakedGTs :: Tagged _ Integer
defaultStakedGTs = Tagged 100000 defStakedGTs = 100000
{- | If 'Parameters.alterOutputStake' is set to true, the
'StakeDatum.stakedAmount' will be set to this.
-}
alteredStakedGTs :: Tagged _ Integer
alteredStakedGTs = 100
-- | Default owner of the stakes.
defOwner :: PubKeyHash
defOwner = signer
-- | How a stake has been used on a particular proposal. -- | How a stake has been used on a particular proposal.
data StakeRole data StakeRole
= -- | The stake was spent to vote for a paraticular option. = -- | The stake was spent to vote for a paraticular option.
Voter Voter
| -- | The stake was used to created the proposal. | -- | The stake was used to create the proposal.
Creator Creator
| -- | The stake was used to both create and vote for the proposal.
Both
| -- | The stake has nothing to do with the proposal. | -- | The stake has nothing to do with the proposal.
Irrelevant Irrelevant
deriving stock (Bounded, Enum, Show)
-- | Parameters for creating a 'TxOut' that unlocks a stake. -- | Parameters for creating a 'TxOut' that unlocks a stake.
data UnlockStakeParameters = UnlockStakeParameters data Parameters = Parameters
{ proposalCount :: Integer { proposalCount :: Integer
-- ^ The number of proposals in the 'TxOut'. -- ^ The number of proposals in the 'TxOut'.
, stakeUsage :: StakeRole , stakeRole :: StakeRole
-- ^ The role of the stake we're unlocking. -- ^ The role of the stake we're unlocking.
, retractVotes :: Bool , retractVotes :: Bool
-- ^ Whether to retract votes or not. -- ^ Whether to retract votes or not.
, removeVoterLock :: Bool
-- ^ Remove the voter locks from the input stake.
, removeCreatorLock :: Bool
-- ^ Remove the creator locks from the input stake.
, proposalStatus :: ProposalStatus , proposalStatus :: ProposalStatus
-- ^ The state of all the proposals. -- ^ The state of all the proposals.
, alterOutputStake :: Bool
} }
instance Show UnlockStakeParameters where -- | Iterate over the proposal id of every proposal, given the number of proposals.
show p = forEachProposalId :: Parameters -> (ProposalId -> a) -> [a]
let role = case p.stakeUsage of forEachProposalId ps = forEachProposalId' ps.proposalCount
Voter -> "voter" where
Creator -> "creator" forEachProposalId' :: Integer -> (ProposalId -> a) -> [a]
_ -> "irrelevant stake" forEachProposalId' 0 _ = error "zero proposal"
forEachProposalId' n f = f . ProposalId <$> [0 .. n - 1]
action = -- | Create locks for the input stake given the parameters.
if p.retractVotes mkInputStakeLocks :: Parameters -> [ProposalLock]
then "unlock stake + retract votes" mkInputStakeLocks ps = mconcat $ forEachProposalId ps $ mkStakeLocksFor ps.stakeRole
else "unlock stake" where
mkStakeLocksFor :: StakeRole -> ProposalId -> [ProposalLock]
mkStakeLocksFor sr pid =
let voted = [Voted pid defVoteFor]
created = [Created pid]
in case sr of
Voter -> voted
Creator -> created
Both -> voted <> created
_ -> []
while = show p.proposalStatus -- | Create locks for the output stake by removing locks from the input locks.
mkOutputStakeLocks :: Parameters -> [ProposalLock]
mkOutputStakeLocks ps =
filter
( \lock -> not $ case lock of
Voted _ _ -> ps.removeVoterLock
Created _ -> ps.removeCreatorLock
)
inputLocks
where
inputLocks = mkInputStakeLocks ps
proposalInfo = mconcat [show p.proposalCount, " proposals"] -- | Create the stake input datum given the parameters.
in mconcat [proposalInfo, ", ", role, ", ", action, ", ", while] mkStakeInputDatum :: Parameters -> StakeDatum
mkStakeInputDatum ps =
StakeDatum
{ stakedAmount = defStakedGTs
, owner = defOwner
, delegatedTo = Nothing
, lockedBy = mkInputStakeLocks ps
}
-- | Create stake output datum given the parameters.
mkStakeOutputDatum :: Parameters -> StakeDatum
mkStakeOutputDatum ps =
let template = mkStakeInputDatum ps
stakedAmount' =
if ps.alterOutputStake
then alteredStakedGTs
else defStakedGTs
in template
{ stakedAmount = stakedAmount'
, lockedBy = mkOutputStakeLocks ps
}
-- | Generate some input proposals and their corresponding output proposals. -- | Generate some input proposals and their corresponding output proposals.
mkProposals :: UnlockStakeParameters -> [(ProposalDatum, ProposalDatum)] mkProposals :: Parameters -> [(ProposalDatum, ProposalDatum)]
mkProposals p = forEachProposalId p.proposalCount $ mkProposalDatumPair p mkProposals ps = forEachProposalId ps $ mkProposalDatumPair ps
-- | Iterate over the proposal id of every proposal, given the number of proposals.
forEachProposalId :: Integer -> (ProposalId -> a) -> [a]
forEachProposalId 0 _ = error "zero proposal"
forEachProposalId n f = f . ProposalId <$> [0 .. n - 1]
-- | Create the input stake and its corresponding output stake.
mkStakeDatumPair :: UnlockStakeParameters -> (StakeDatum, StakeDatum)
mkStakeDatumPair c =
let output =
StakeDatum
{ stakedAmount = defaultStakedGTs
, owner = signer
, lockedBy = []
}
inputLocks = join $ forEachProposalId c.proposalCount (mkStakeLocks c.stakeUsage)
input = output {lockedBy = inputLocks}
in (input, output)
where
mkStakeLocks :: StakeRole -> ProposalId -> [ProposalLock]
mkStakeLocks Voter pid = [ProposalLock defaultVoteFor pid]
mkStakeLocks Creator pid =
map (`ProposalLock` pid) $
AssocMap.keys $ getProposalVotes votesTemplate
mkStakeLocks _ _ = []
-- | Create the input proposal datum. -- | Create the input proposal datum.
mkProposalInputDatum :: UnlockStakeParameters -> ProposalId -> ProposalDatum mkProposalInputDatum :: Parameters -> ProposalId -> ProposalDatum
mkProposalInputDatum p pid = fst $ mkProposalDatumPair p pid mkProposalInputDatum p pid = fst $ mkProposalDatumPair p pid
-- | Create the input stake datum.
mkStakeInputDatum :: UnlockStakeParameters -> StakeDatum
mkStakeInputDatum = fst . mkStakeDatumPair
-- | Create a input proposal and its corresponding output proposal. -- | Create a input proposal and its corresponding output proposal.
mkProposalDatumPair :: mkProposalDatumPair ::
UnlockStakeParameters -> Parameters ->
ProposalId -> ProposalId ->
(ProposalDatum, ProposalDatum) (ProposalDatum, ProposalDatum)
mkProposalDatumPair params pid = mkProposalDatumPair params pid =
let owner = signer let inputVotes = mkInputVotes params.stakeRole $ untag defStakedGTs
inputVotes = mkInputVotes params.stakeUsage $ untag defaultStakedGTs
input = input =
ProposalDatum ProposalDatum
{ proposalId = pid { proposalId = pid
, effects = emptyEffectFor votesTemplate , effects = emptyEffectFor votesTemplate
, status = params.proposalStatus , status = params.proposalStatus
, cosigners = [owner] , cosigners = [defOwner]
, thresholds = def , thresholds = def
, votes = inputVotes , votes = inputVotes
, timingConfig = def , timingConfig = def
@ -195,82 +234,317 @@ mkProposalDatumPair params pid =
-- The staked amount/votes. -- The staked amount/votes.
Integer -> Integer ->
ProposalVotes ProposalVotes
mkInputVotes Voter vc =
ProposalVotes $
updateMap (Just . const vc) defaultVoteFor $
getProposalVotes votesTemplate
mkInputVotes Creator _ = mkInputVotes Creator _ =
ProposalVotes $ ProposalVotes $
updateMap (Just . const 1000) defaultVoteFor $ updateMap (Just . const 1000) defVoteFor $
getProposalVotes votesTemplate
mkInputVotes Irrelevant _ = votesTemplate
mkInputVotes _ vc =
ProposalVotes $
updateMap (Just . const vc) defVoteFor $
getProposalVotes votesTemplate getProposalVotes votesTemplate
mkInputVotes _ _ = votesTemplate
-- | Create a 'TxInfo' that tries to unlock a stake. -- | Create a 'TxInfo' that tries to unlock a stake.
unlockStake :: UnlockStakeParameters -> TxInfo unlockStake :: forall b. CombinableBuilder b => Parameters -> b
unlockStake p = unlockStake ps =
let pst = Value.singleton proposalPolicySymbol "" 1 let pst = Value.singleton proposalPolicySymbol "" 1
sst = Value.assetClassValue stakeAssetClass 1 sst = Value.assetClassValue stakeAssetClass 1
pIODatums = mkProposals p pIODatums = mkProposals ps
(sInDatum, sOutDatum) = mkStakeDatumPair p
proposals = proposals =
foldMap foldMap
( \(i, o) -> ( \((i, o), idx) ->
mconcat mconcat
@BaseBuilder
[ input $ [ input $
script proposalValidatorHash mconcat
. withValue pst [ script proposalValidatorHash
. withDatum i , withValue pst
. withTxId (txOutRefId proposalRef) , withDatum i
. withRefIndex (txOutRefIdx proposalRef + coerce i.proposalId) , withOutRef (mkProposalRef idx)
]
, output $ , output $
script proposalValidatorHash mconcat
. withValue (sortValue $ pst <> minAda) [ script proposalValidatorHash
. withDatum o , withValue (sortValue $ pst <> minAda)
, withDatum o
]
] ]
) )
pIODatums (zip pIODatums [0 ..])
stakeValue = stakeValue =
sortValue $ sortValue $
mconcat mconcat
[ Value.assetClassValue [ Value.assetClassValue
(untag stake.gtClassRef) (untag governor.gtClassRef)
(untag defaultStakedGTs) (untag defStakedGTs)
, sst , sst
, minAda , minAda
] ]
sInDatum = mkStakeInputDatum ps
sOutDatum = mkStakeOutputDatum ps
stakes = stakes =
mconcat @BaseBuilder mconcat
[ input $ [ input $
script stakeValidatorHash mconcat
. withValue stakeValue [ script stakeValidatorHash
. withDatum sInDatum , withValue stakeValue
. withTxId (txOutRefId stakeRef) , withDatum sInDatum
. withRefIndex (txOutRefIdx stakeRef) , withOutRef stakeRef
]
, output $ , output $
script stakeValidatorHash mconcat
. withValue stakeValue [ script stakeValidatorHash
. withDatum sOutDatum , withValue stakeValue
, withDatum sOutDatum
]
] ]
builder = builder =
mconcat @BaseBuilder mconcat
[ txId "388bc0b897b3dadcd479da4c88291de4113a50b72ddbed001faf7fc03f11bc52" [ txId "388bc0b897b3dadcd479da4c88291de4113a50b72ddbed001faf7fc03f11bc52"
, proposals , proposals
, stakes , stakes
, signedWith defOwner
] ]
in buildTxInfoUnsafe builder in builder
-- | Create a test case that tests the proposal validator's @'Unlock' _@ redeemer. -- | Reference to the stake UTXO.
mkProposalValidatorTestCase :: UnlockStakeParameters -> Bool -> SpecificationTree stakeRef :: TxOutRef
mkProposalValidatorTestCase p shouldSucceed = stakeRef = TxOutRef stakeTxRef 1
let datum = mkProposalInputDatum p $ ProposalId 0
redeemer = Unlock (ResultTag 0) -- | Generate the reference to a proposal UTXOs, given the index of the proposal.
name = show p mkProposalRef :: Int -> TxOutRef
scriptContext = ScriptContext (unlockStake p) (Spending proposalRef) mkProposalRef offset = TxOutRef stakeTxRef $ 2 + fromIntegral offset
f = if shouldSucceed then validatorSucceedsWith else validatorFailsWith
in f name (proposalValidator Shared.proposal) datum redeemer scriptContext -- | Proposal redeemer used by 'mkTestTree', in this case it's always 'Unlock'.
proposalRedeemer :: ProposalRedeemer
proposalRedeemer = Unlock
-- | Stake redeemer used by 'mkTestTree', in this case it's always 'RetractVotes'.
stakeRedeemer :: StakeRedeemer
stakeRedeemer = RetractVotes
--------------------------------------------------------------------------------
{- | Legal parameters that retract votes while the proposals is in 'VotingReady'
state, and also remove voter locks from the stake, which is
used to vote on the proposals.
-}
mkVoterRetractVotesWhileVotingParameters :: Integer -> Parameters
mkVoterRetractVotesWhileVotingParameters nProposals =
Parameters
{ proposalCount = nProposals
, stakeRole = Voter
, retractVotes = True
, removeVoterLock = True
, removeCreatorLock = False
, proposalStatus = VotingReady
, alterOutputStake = False
}
{- | Legal parameters that retract votes while the proposals is in 'VotingReady'
state, and also remove voter locks from the stake, which is
used to both create and vote on the proposals.
-}
mkVoterCreatorRetractVotesWhileVotingParameters :: Integer -> Parameters
mkVoterCreatorRetractVotesWhileVotingParameters nProposals =
Parameters
{ proposalCount = nProposals
, stakeRole = Both
, retractVotes = True
, removeVoterLock = True
, removeCreatorLock = False
, proposalStatus = VotingReady
, alterOutputStake = False
}
{- | Legal parameters that remove creator locks from the stake while the
proposals is in 'Finished' state. The stake was only used for creating
the proposals.
-}
mkCreatorRemoveCreatorLocksWhenFinishedParameters :: Integer -> Parameters
mkCreatorRemoveCreatorLocksWhenFinishedParameters nProposals =
Parameters
{ proposalCount = nProposals
, stakeRole = Creator
, retractVotes = False
, removeVoterLock = False
, removeCreatorLock = True
, proposalStatus = Finished
, alterOutputStake = False
}
{- | Legal parameters that remove voter and creator locks from the stake while
the proposals is in 'Finished' state. The stake was used for creating
and voting on the proposals.
-}
mkVoterCreatorRemoveAllLocksWhenFinishedParameters :: Integer -> Parameters
mkVoterCreatorRemoveAllLocksWhenFinishedParameters nProposals =
Parameters
{ proposalCount = nProposals
, stakeRole = Both
, retractVotes = False
, removeVoterLock = True
, removeCreatorLock = True
, proposalStatus = Finished
, alterOutputStake = False
}
{- Legal parameters that remove voter locks from the stake after the voting
phrase. The stake was used only for voting on the proposals.
-}
mkVoterUnlockStakeAfterVotingParameters :: Integer -> [Parameters]
mkVoterUnlockStakeAfterVotingParameters nProposals =
map
( \st ->
Parameters
{ proposalCount = nProposals
, stakeRole = Voter
, retractVotes = False
, removeVoterLock = True
, removeCreatorLock = False
, proposalStatus = st
, alterOutputStake = False
}
)
[Locked, Finished]
{- Legal parameters that remove voter locks whenproposals are in phrase.
The stake was used for crating and voting on the proposals.
-}
mkVoterCreatorRemoveVoteLocksWhenLockedParameters :: Integer -> Parameters
mkVoterCreatorRemoveVoteLocksWhenLockedParameters nProposals =
Parameters
{ proposalCount = nProposals
, stakeRole = Both
, retractVotes = False
, removeVoterLock = True
, removeCreatorLock = False
, proposalStatus = Locked
, alterOutputStake = False
}
{- | Illegal parameters that retract votes when the proposals are not in voting
phrase.
-}
mkRetractVotesWhileNotVoting :: Integer -> [Parameters]
mkRetractVotesWhileNotVoting nProposals = do
role <- enumFrom Voter
status <- [Draft, Locked, Finished]
pure $
Parameters
{ proposalCount = nProposals
, stakeRole = role
, retractVotes = True
, removeVoterLock = True
, removeCreatorLock = False
, proposalStatus = status
, alterOutputStake = False
}
{- | Illegal parameter that try to unlock a stake that has nothing to do with
the proposals.
-}
mkUnockIrrelevantStakeParameters :: Integer -> [Parameters]
mkUnockIrrelevantStakeParameters nProposals = do
status <- [Draft, VotingReady, Locked, Finished]
retractVotes <- [True, False]
pure $
Parameters
{ proposalCount = nProposals
, stakeRole = Irrelevant
, retractVotes = retractVotes
, removeVoterLock = True
, removeCreatorLock = True
, proposalStatus = status
, alterOutputStake = False
}
{- | Illegal parameters that remove the creator locks before the proposals are
'Finished'.
-}
mkRemoveCreatorLockBeforeFinishedParameters :: Integer -> [Parameters]
mkRemoveCreatorLockBeforeFinishedParameters nProposals = do
status <- [Draft, VotingReady, Locked]
pure $
Parameters
{ proposalCount = nProposals
, stakeRole = Creator
, retractVotes = False
, removeVoterLock = False
, removeCreatorLock = True
, proposalStatus = status
, alterOutputStake = False
}
{- | Illegal parameters that try to retract votes with a stake that was only used
for creating the proposals.
-}
mkRetractVotesWithCreatorStakeParamaters :: Integer -> Parameters
mkRetractVotesWithCreatorStakeParamaters nProposals =
Parameters
{ proposalCount = nProposals
, stakeRole = Creator
, retractVotes = True
, removeVoterLock = True
, removeCreatorLock = True
, proposalStatus = VotingReady
, alterOutputStake = False
}
{- | Illegal parameters that try to change the 'StakeDatum.stakedAmount' field of
the output stake datum.
-}
mkAlterStakeParameters :: Integer -> [Parameters]
mkAlterStakeParameters nProposals = do
role <- enumFrom Voter
status <- [Draft, Locked, Finished]
pure $
Parameters
{ proposalCount = nProposals
, stakeRole = role
, retractVotes = True
, removeVoterLock = True
, removeCreatorLock = False
, proposalStatus = status
, alterOutputStake = True
}
--------------------------------------------------------------------------------
{- | Create a test tree that runs both the stake validator and the proposal
validator.
-}
mkTestTree :: String -> Parameters -> Bool -> SpecificationTree
mkTestTree name ps isValid = group name [stake, proposal]
where
spend = mkSpending unlockStake ps
stake =
testValidator
(not ps.alterOutputStake)
"stake"
agoraScripts.compiledStakeValidator
(mkStakeInputDatum ps)
stakeRedeemer
(spend stakeRef)
proposal =
let idx = 0
pid = ProposalId $ fromIntegral idx
ref = mkProposalRef idx
in testValidator
isValid
"proposal"
agoraScripts.compiledProposalValidator
(mkProposalInputDatum ps pid)
proposalRedeemer
(spend ref)

View file

@ -0,0 +1,292 @@
{- |
Module : Sample.Proposal.Vote
Maintainer : connor@mlabs.city
Description: Generate sample data for testing the functionalities of voting on proposals.
Sample and utilities for testing the functionalities of voting on proposals.
-}
module Sample.Proposal.Vote (
validVoteParameters,
mkTestTree,
validVoteAsDelegateParameters,
) where
import Agora.Governor (Governor (..))
import Agora.Proposal (
ProposalDatum (..),
ProposalId (ProposalId),
ProposalRedeemer (Vote),
ProposalStatus (VotingReady),
ProposalVotes (ProposalVotes),
ResultTag (ResultTag),
)
import Agora.Proposal.Time (
ProposalStartingTime (ProposalStartingTime),
ProposalTimingConfig (draftTime, votingTime),
)
import Agora.Scripts (AgoraScripts (..))
import Agora.Stake (
ProposalLock (..),
StakeDatum (..),
StakeRedeemer (PermitVote),
)
import Data.Default (Default (def))
import Data.Tagged (Tagged (Tagged), untag)
import Plutarch.Context (
input,
output,
script,
signedWith,
timeRange,
txId,
withDatum,
withOutRef,
withValue,
)
import PlutusLedgerApi.V1 (
PubKeyHash,
TxOutRef (TxOutRef),
)
import PlutusLedgerApi.V1.Value qualified as Value
import PlutusTx.AssocMap qualified as AssocMap
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef)
import Sample.Shared (
agoraScripts,
governor,
minAda,
proposalPolicySymbol,
proposalValidatorHash,
signer,
stakeAssetClass,
stakeValidatorHash,
)
import Test.Specification (
SpecificationTree,
group,
testValidator,
validatorSucceedsWith,
)
import Test.Util (CombinableBuilder, closedBoundedInterval, mkSpending, pubKeyHashes, sortValue, updateMap)
-- | Reference to the proposal UTXO.
proposalRef :: TxOutRef
proposalRef = TxOutRef proposalTxRef 0
-- | Reference to the stake UTXO.
stakeRef :: TxOutRef
stakeRef = TxOutRef stakeTxRef 1
-- | Parameters for creating a voting transaction.
data Parameters = Parameters
{ voteFor :: ResultTag
-- ^ The outcome the transaction is voting for.
, voteCount :: Integer
-- ^ The count of votes.
, voteAsDelegate :: Bool
-- ^ Delegate the stake and use it to vote.
}
-- | The public key hash of the stake owner.
stakeOwner :: PubKeyHash
stakeOwner = signer
-- | The votes of the input proposals.
initialVotes :: AssocMap.Map ResultTag Integer
initialVotes =
AssocMap.fromList
[ (ResultTag 0, 42)
, (ResultTag 1, 4242)
]
-- | The input proposal datum.
proposalInputDatum :: ProposalDatum
proposalInputDatum =
ProposalDatum
{ proposalId = ProposalId 42
, effects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
]
, status = VotingReady
, cosigners = [stakeOwner]
, thresholds = def
, votes = ProposalVotes initialVotes
, timingConfig = def
, startingTime = ProposalStartingTime 0
}
-- | The locks of the input stake.
existingLocks :: [ProposalLock]
existingLocks =
[ Voted (ProposalId 0) (ResultTag 0)
, Voted (ProposalId 1) (ResultTag 2)
]
delegate :: PubKeyHash
delegate = head pubKeyHashes
{- | Set the 'StakeDatum.stakedAmount' according to the number of votes being
casted.
-}
mkStakeInputDatum :: Parameters -> StakeDatum
mkStakeInputDatum params =
StakeDatum
{ stakedAmount = Tagged params.voteCount
, owner = stakeOwner
, delegatedTo =
if params.voteAsDelegate
then Just delegate
else Nothing
, lockedBy = existingLocks
}
-- | Create the proposal redeemer. In this case @'Vote' _@ will always be used.
mkProposalRedeemer :: Parameters -> ProposalRedeemer
mkProposalRedeemer = Vote . voteFor
-- | Place new proposal locks on the stake.
mkNewLock :: Parameters -> ProposalLock
mkNewLock = Voted proposalInputDatum.proposalId . voteFor
{- | The stake redeemer that is used in 'mkTestTree'. In this case it'll always be
'PermitVote'.
-}
stakeRedeemer :: StakeRedeemer
stakeRedeemer = PermitVote
-- | Create a valid transaction that votes on a propsal, given the parameters.
vote :: forall b. CombinableBuilder b => Parameters -> b
vote params =
let pst = Value.singleton proposalPolicySymbol "" 1
sst = Value.assetClassValue stakeAssetClass 1
---
stakeInputDatum = mkStakeInputDatum params
---
updatedVotes :: AssocMap.Map ResultTag Integer
updatedVotes = updateMap (Just . (+ params.voteCount)) params.voteFor initialVotes
---
proposalOutputDatum :: ProposalDatum
proposalOutputDatum =
proposalInputDatum
{ votes = ProposalVotes updatedVotes
}
---
-- Off-chain code should do exactly like this: prepend new lock toStatus the list.
updatedLocks :: [ProposalLock]
updatedLocks = mkNewLock params : existingLocks
---
stakeOutputDatum :: StakeDatum
stakeOutputDatum =
stakeInputDatum
{ lockedBy = updatedLocks
}
---
validTimeRange =
closedBoundedInterval
((def :: ProposalTimingConfig).draftTime + 1)
((def :: ProposalTimingConfig).votingTime - 1)
---
stakeValue =
sortValue $
sst
<> Value.assetClassValue (untag governor.gtClassRef) params.voteCount
<> minAda
signer =
if params.voteAsDelegate
then delegate
else stakeOwner
builder =
mconcat
[ txId "827598fb2d69a896bbd9e645bb14c307df907f422b39eecbe4d6329bc30b428c"
, signedWith signer
, timeRange validTimeRange
, input $
mconcat
[ script proposalValidatorHash
, withValue pst
, withDatum proposalInputDatum
, withOutRef proposalRef
]
, input $
mconcat
[ script stakeValidatorHash
, withValue stakeValue
, withDatum stakeInputDatum
, withOutRef stakeRef
]
, output $
mconcat
[ script proposalValidatorHash
, withValue pst
, withDatum proposalOutputDatum
]
, output $
mconcat
[ script stakeValidatorHash
, withValue stakeValue
, withDatum stakeOutputDatum
]
]
in builder
---
-- | Valida parameters that vote on the proposal.
validVoteParameters :: Parameters
validVoteParameters =
Parameters
{ voteFor = ResultTag 0
, voteCount = 27
, voteAsDelegate = False
}
validVoteAsDelegateParameters :: Parameters
validVoteAsDelegateParameters =
validVoteParameters
{ voteAsDelegate = True
}
---
{- | Create a test tree that runs the stake validator and proposal validator to
test the voting functionalities.
-}
mkTestTree :: String -> Parameters -> Bool -> SpecificationTree
mkTestTree name ps isValid = group name [proposal, stake]
where
spend = mkSpending vote ps
proposal =
testValidator
isValid
"proposal"
agoraScripts.compiledProposalValidator
proposalInputDatum
(mkProposalRedeemer ps)
(spend proposalRef)
stake =
let stakeInputDatum = mkStakeInputDatum ps
in validatorSucceedsWith
"stake"
agoraScripts.compiledStakeValidator
stakeInputDatum
stakeRedeemer
(spend stakeRef)

View file

@ -12,11 +12,15 @@ module Sample.Shared (
signer, signer,
signer2, signer2,
minAda, minAda,
deterministicTracingConfing,
mkEffect,
-- * Agora Scripts
agoraScripts,
-- * Components -- * Components
-- ** Stake -- ** Stake
stake,
stakeAssetClass, stakeAssetClass,
stakeValidatorHash, stakeValidatorHash,
stakeAddress, stakeAddress,
@ -33,14 +37,12 @@ module Sample.Shared (
gstUTXORef, gstUTXORef,
-- ** Proposal -- ** Proposal
proposal,
proposalPolicySymbol, proposalPolicySymbol,
proposalValidatorHash, proposalValidatorHash,
proposalValidatorAddress, proposalValidatorAddress,
proposalStartingTimeFromTimeRange, proposalStartingTimeFromTimeRange,
-- ** Authority -- ** Authority
authorityToken,
authorityTokenSymbol, authorityTokenSymbol,
-- ** Treasury -- ** Treasury
@ -53,38 +55,29 @@ module Sample.Shared (
wrongEffHash, wrongEffHash,
) where ) where
import Agora.AuthorityToken (AuthorityToken) import Agora.Bootstrap qualified as Bootstrap
import Agora.Effect.NoOp (noOpValidator) import Agora.Effect.NoOp (noOpValidator)
import Agora.Governor (Governor (Governor)) import Agora.Governor (Governor (Governor))
import Agora.Governor.Scripts ( import Agora.Proposal (ProposalThresholds (..))
authorityTokenFromGovernor,
authorityTokenSymbolFromGovernor,
governorPolicy,
governorSTAssetClassFromGovernor,
governorValidator,
governorValidatorHash,
proposalFromGovernor,
proposalSTSymbolFromGovernor,
proposalValidatorHashFromGovernor,
stakeFromGovernor,
stakeSTAssetClassFromGovernor,
stakeSTSymbolFromGovernor,
stakeValidatorHashFromGovernor,
)
import Agora.Proposal (Proposal (..), ProposalThresholds (..))
import Agora.Proposal.Time ( import Agora.Proposal.Time (
MaxTimeRangeWidth (..), MaxTimeRangeWidth (..),
ProposalStartingTime (ProposalStartingTime), ProposalStartingTime (ProposalStartingTime),
ProposalTimingConfig (..), ProposalTimingConfig (..),
) )
import Agora.Stake (Stake (..)) import Agora.Scripts qualified as Scripts
import Agora.Treasury (treasuryValidator) import Agora.Treasury (treasuryValidator)
import Agora.Utils (validatorHashToTokenName) import Agora.Utils (
CompiledEffect (CompiledEffect),
CompiledMintingPolicy (getCompiledMintingPolicy),
CompiledValidator (getCompiledValidator),
validatorHashToTokenName,
)
import Data.Default.Class (Default (..)) import Data.Default.Class (Default (..))
import Data.Tagged (Tagged (..)) import Data.Tagged (Tagged (..))
import Plutarch (Config (..), TracingMode (DetTracing))
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
PValidator,
mintingPolicySymbol, mintingPolicySymbol,
mkMintingPolicy,
mkValidator, mkValidator,
validatorHash, validatorHash,
) )
@ -110,24 +103,13 @@ import PlutusLedgerApi.V1.Value qualified as Value (
assetClass, assetClass,
singleton, singleton,
) )
import PlutusTx qualified
stake :: Stake -- Plutarch compiler configauration.
stake = stakeFromGovernor governor -- TODO: add the ability to change this value. Maybe wrap everything in a
-- Reader monad?
stakeSymbol :: CurrencySymbol deterministicTracingConfing :: Config
stakeSymbol = stakeSTSymbolFromGovernor governor deterministicTracingConfing = Config DetTracing
stakeAssetClass :: AssetClass
stakeAssetClass = stakeSTAssetClassFromGovernor governor
stakeValidatorHash :: ValidatorHash
stakeValidatorHash = stakeValidatorHashFromGovernor governor
stakeAddress :: Address
stakeAddress = Address (ScriptCredential stakeValidatorHash) Nothing
gstUTXORef :: TxOutRef
gstUTXORef = TxOutRef "f28cd7145c24e66fd5bcd2796837aeb19a48a2656e7833c88c62a2d0450bd00d" 0
governor :: Governor governor :: Governor
governor = Governor oref gt mc governor = Governor oref gt mc
@ -138,31 +120,46 @@ governor = Governor oref gt mc
Value.assetClass Value.assetClass
"da8c30857834c6ae7203935b89278c532b3995245295456f993e1d24" "da8c30857834c6ae7203935b89278c532b3995245295456f993e1d24"
"LQ" "LQ"
mc = 6 mc = 20
agoraScripts :: Scripts.AgoraScripts
agoraScripts = Bootstrap.agoraScripts deterministicTracingConfing governor
stakeSymbol :: CurrencySymbol
stakeSymbol = Scripts.stakeSTSymbol agoraScripts
stakeAssetClass :: AssetClass
stakeAssetClass = Scripts.stakeSTAssetClass agoraScripts
stakeValidatorHash :: ValidatorHash
stakeValidatorHash = Scripts.stakeValidatorHash agoraScripts
stakeAddress :: Address
stakeAddress = Address (ScriptCredential stakeValidatorHash) Nothing
gstUTXORef :: TxOutRef
gstUTXORef = TxOutRef "f28cd7145c24e66fd5bcd2796837aeb19a48a2656e7833c88c62a2d0450bd00d" 0
govPolicy :: MintingPolicy govPolicy :: MintingPolicy
govPolicy = mkMintingPolicy (governorPolicy governor) govPolicy = getCompiledMintingPolicy $ agoraScripts.compiledGovernorPolicy
govValidator :: Validator govValidator :: Validator
govValidator = mkValidator (governorValidator governor) govValidator = getCompiledValidator $ agoraScripts.compiledGovernorValidator
govSymbol :: CurrencySymbol govSymbol :: CurrencySymbol
govSymbol = mintingPolicySymbol govPolicy govSymbol = mintingPolicySymbol govPolicy
govAssetClass :: AssetClass govAssetClass :: AssetClass
govAssetClass = governorSTAssetClassFromGovernor governor govAssetClass = Scripts.governorSTAssetClass agoraScripts
govValidatorHash :: ValidatorHash govValidatorHash :: ValidatorHash
govValidatorHash = governorValidatorHash governor govValidatorHash = Scripts.governorValidatorHash agoraScripts
govValidatorAddress :: Address govValidatorAddress :: Address
govValidatorAddress = scriptHashAddress govValidatorHash govValidatorAddress = scriptHashAddress govValidatorHash
proposal :: Proposal
proposal = proposalFromGovernor governor
proposalPolicySymbol :: CurrencySymbol proposalPolicySymbol :: CurrencySymbol
proposalPolicySymbol = proposalSTSymbolFromGovernor governor proposalPolicySymbol = Scripts.proposalSTSymbol agoraScripts
-- | A sample 'PubKeyHash'. -- | A sample 'PubKeyHash'.
signer :: PubKeyHash signer :: PubKeyHash
@ -173,7 +170,7 @@ signer2 :: PubKeyHash
signer2 = "8a30896c4fd5e79843e4ca1bd2cdbaa36f8c0bc3be74012141420192" signer2 = "8a30896c4fd5e79843e4ca1bd2cdbaa36f8c0bc3be74012141420192"
proposalValidatorHash :: ValidatorHash proposalValidatorHash :: ValidatorHash
proposalValidatorHash = proposalValidatorHashFromGovernor governor proposalValidatorHash = Scripts.proposalValidatoHash agoraScripts
proposalValidatorAddress :: Address proposalValidatorAddress :: Address
proposalValidatorAddress = scriptHashAddress proposalValidatorHash proposalValidatorAddress = scriptHashAddress proposalValidatorHash
@ -186,14 +183,11 @@ instance Default ProposalThresholds where
ProposalThresholds ProposalThresholds
{ execute = Tagged 1000 { execute = Tagged 1000
, create = Tagged 1 , create = Tagged 1
, vote = Tagged 10 , vote = Tagged 100
} }
authorityToken :: AuthorityToken
authorityToken = authorityTokenFromGovernor governor
authorityTokenSymbol :: CurrencySymbol authorityTokenSymbol :: CurrencySymbol
authorityTokenSymbol = authorityTokenSymbolFromGovernor governor authorityTokenSymbol = Scripts.authorityTokenSymbol agoraScripts
{- | Default value of 'Agora.Governor.GovernorDatum.proposalTimings'. {- | Default value of 'Agora.Governor.GovernorDatum.proposalTimings'.
For testing purpose only. For testing purpose only.
@ -222,6 +216,9 @@ proposalStartingTimeFromTimeRange
ProposalStartingTime $ (l + u) `div` 2 ProposalStartingTime $ (l + u) `div` 2
proposalStartingTimeFromTimeRange _ = error "Given time range should be finite and closed" proposalStartingTimeFromTimeRange _ = error "Given time range should be finite and closed"
mkEffect :: (PlutusTx.ToData datum) => ClosedTerm PValidator -> CompiledEffect datum
mkEffect v = CompiledEffect $ mkValidator deterministicTracingConfing v
------------------------------------------------------------------ ------------------------------------------------------------------
treasuryOut :: TxOut treasuryOut :: TxOut
@ -239,7 +236,7 @@ gatCs :: CurrencySymbol
gatCs = "73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049" gatCs = "73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049"
trValidator :: Validator trValidator :: Validator
trValidator = mkValidator (treasuryValidator gatCs) trValidator = mkValidator def (treasuryValidator gatCs)
-- | `ScriptCredential` used for the dummy treasury validator. -- | `ScriptCredential` used for the dummy treasury validator.
trCredential :: Credential trCredential :: Credential
@ -251,7 +248,7 @@ gatTn = validatorHashToTokenName $ validatorHash mockTrEffect
-- | Mock treasury effect script, used for testing. -- | Mock treasury effect script, used for testing.
mockTrEffect :: Validator mockTrEffect :: Validator
mockTrEffect = mkValidator $ noOpValidator gatCs mockTrEffect = mkValidator def $ noOpValidator gatCs
-- | Mock treasury effect validator hash -- | Mock treasury effect validator hash
mockTrEffectHash :: ValidatorHash mockTrEffectHash :: ValidatorHash

View file

@ -6,10 +6,8 @@ Description: Sample based testing for Stake utxos
This module tests primarily the happy path for Stake creation This module tests primarily the happy path for Stake creation
-} -}
module Sample.Stake ( module Sample.Stake (
stake,
stakeAssetClass, stakeAssetClass,
stakeSymbol, stakeSymbol,
validatorHashTN,
signer, signer,
-- * Script contexts -- * Script contexts
@ -20,14 +18,12 @@ module Sample.Stake (
DepositWithdrawExample (..), DepositWithdrawExample (..),
) where ) where
import Agora.Governor (Governor (gtClassRef))
import Agora.SafeMoney (GTTag) import Agora.SafeMoney (GTTag)
import Agora.Stake ( import Agora.Stake (
Stake (gtClassRef),
StakeDatum (StakeDatum, stakedAmount), StakeDatum (StakeDatum, stakedAmount),
) )
import Agora.Stake.Scripts (stakeValidator)
import Data.Tagged (Tagged, untag) import Data.Tagged (Tagged, untag)
import Plutarch.Api.V1 (mkValidator, validatorHash)
import Plutarch.Context ( import Plutarch.Context (
MintingBuilder, MintingBuilder,
SpendingBuilder, SpendingBuilder,
@ -40,8 +36,9 @@ import Plutarch.Context (
signedWith, signedWith,
txId, txId,
withDatum, withDatum,
withSpending, withMinting,
withTxId, withOutRef,
withSpendingOutRef,
withValue, withValue,
) )
import PlutusLedgerApi.V1 ( import PlutusLedgerApi.V1 (
@ -49,32 +46,28 @@ import PlutusLedgerApi.V1 (
ScriptContext (..), ScriptContext (..),
ScriptPurpose (Minting), ScriptPurpose (Minting),
ToData (toBuiltinData), ToData (toBuiltinData),
TokenName (TokenName),
TxInfo (txInfoData, txInfoSignatories), TxInfo (txInfoData, txInfoSignatories),
ValidatorHash (ValidatorHash),
) )
import PlutusLedgerApi.V1.Contexts (TxOutRef (..))
import PlutusLedgerApi.V1.Value qualified as Value ( import PlutusLedgerApi.V1.Value qualified as Value (
assetClassValue, assetClassValue,
singleton, singleton,
) )
import Sample.Shared ( import Sample.Shared (
governor,
signer, signer,
stake,
stakeAssetClass, stakeAssetClass,
stakeSymbol, stakeSymbol,
stakeValidatorHash, stakeValidatorHash,
) )
import Test.Util (sortValue)
-- | 'TokenName' that represents the hash of the 'Stake' validator.
validatorHashTN :: TokenName
validatorHashTN = let ValidatorHash vh = validatorHash (mkValidator $ stakeValidator stake) in TokenName vh
-- | This script context should be a valid transaction. -- | This script context should be a valid transaction.
stakeCreation :: ScriptContext stakeCreation :: ScriptContext
stakeCreation = stakeCreation =
let st = Value.assetClassValue stakeAssetClass 1 -- Stake ST let st = Value.assetClassValue stakeAssetClass 1 -- Stake ST
datum :: StakeDatum datum :: StakeDatum
datum = StakeDatum 424242424242 signer [] datum = StakeDatum 424242424242 signer Nothing []
builder :: MintingBuilder builder :: MintingBuilder
builder = builder =
@ -83,9 +76,12 @@ stakeCreation =
, signedWith signer , signedWith signer
, mint st , mint st
, output $ , output $
script stakeValidatorHash mconcat
. withValue (st <> Value.singleton "da8c30857834c6ae7203935b89278c532b3995245295456f993e1d24" "LQ" 424242424242) [ script stakeValidatorHash
. withDatum datum , withValue (st <> Value.singleton "da8c30857834c6ae7203935b89278c532b3995245295456f993e1d24" "LQ" 424242424242)
, withDatum datum
]
, withMinting stakeSymbol
] ]
in buildMintingUnsafe builder in buildMintingUnsafe builder
@ -93,7 +89,7 @@ stakeCreation =
stakeCreationWrongDatum :: ScriptContext stakeCreationWrongDatum :: ScriptContext
stakeCreationWrongDatum = stakeCreationWrongDatum =
let datum :: Datum let datum :: Datum
datum = Datum (toBuiltinData $ StakeDatum 4242424242424242 signer []) -- Too much GT datum = Datum (toBuiltinData $ StakeDatum 4242424242424242 signer Nothing []) -- Too much GT
in ScriptContext in ScriptContext
{ scriptContextTxInfo = stakeCreation.scriptContextTxInfo {txInfoData = [("", datum)]} { scriptContextTxInfo = stakeCreation.scriptContextTxInfo {txInfoData = [("", datum)]}
, scriptContextPurpose = Minting stakeSymbol , scriptContextPurpose = Minting stakeSymbol
@ -125,11 +121,14 @@ stakeDepositWithdraw :: DepositWithdrawExample -> ScriptContext
stakeDepositWithdraw config = stakeDepositWithdraw config =
let st = Value.assetClassValue stakeAssetClass 1 -- Stake ST let st = Value.assetClassValue stakeAssetClass 1 -- Stake ST
stakeBefore :: StakeDatum stakeBefore :: StakeDatum
stakeBefore = StakeDatum config.startAmount signer [] stakeBefore = StakeDatum config.startAmount signer Nothing []
stakeAfter :: StakeDatum stakeAfter :: StakeDatum
stakeAfter = stakeBefore {stakedAmount = stakeBefore.stakedAmount + config.delta} stakeAfter = stakeBefore {stakedAmount = stakeBefore.stakedAmount + config.delta}
stakeRef :: TxOutRef
stakeRef = TxOutRef "0ffef57e30cc604342c738e31e0451593837b313e7bfb94b0922b142782f98e6" 1
builder :: SpendingBuilder builder :: SpendingBuilder
builder = builder =
mconcat mconcat
@ -137,17 +136,26 @@ stakeDepositWithdraw config =
, signedWith signer , signedWith signer
, mint st , mint st
, input $ , input $
script stakeValidatorHash mconcat
. withValue (st <> Value.assetClassValue (untag stake.gtClassRef) (untag stakeBefore.stakedAmount)) [ script stakeValidatorHash
. withDatum stakeAfter , withValue
. withTxId "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be" ( sortValue $
st
<> Value.assetClassValue (untag governor.gtClassRef) (untag stakeBefore.stakedAmount)
)
, withDatum stakeAfter
, withOutRef stakeRef
]
, output $ , output $
script stakeValidatorHash mconcat
. withValue (st <> Value.assetClassValue (untag stake.gtClassRef) (untag stakeAfter.stakedAmount)) [ script stakeValidatorHash
. withDatum stakeAfter , withValue
, withSpending $ ( sortValue $
script stakeValidatorHash st
. withValue (st <> Value.assetClassValue (untag stake.gtClassRef) (untag stakeBefore.stakedAmount)) <> Value.assetClassValue (untag governor.gtClassRef) (untag stakeAfter.stakedAmount)
. withDatum stakeAfter )
, withDatum stakeAfter
]
, withSpendingOutRef stakeRef
] ]
in buildSpendingUnsafe builder in buildSpendingUnsafe builder

View file

@ -0,0 +1,209 @@
{- |
Module : Sample.Stake.SetDelegate
Maintainer : connor@mlabs.city
Description: Generate sample data for testing the functionalities of setting the delegate.
Sample and utilities for testing the functionalities of setting the delegate.
-}
module Sample.Stake.SetDelegate (
Parameters (..),
setDelegate,
mkStakeRedeemer,
mkStakeInputDatum,
mkTestCase,
overrideExistingDelegateParameters,
clearDelegateParameters,
setDelegateParameters,
invalidOutputStakeDatumParameters,
ownerDoesntSignParameters,
delegateToOwnerParameters,
) where
import Agora.Governor (Governor (gtClassRef))
import Agora.Scripts (AgoraScripts (..))
import Agora.Stake (
StakeDatum (..),
StakeRedeemer (ClearDelegate, DelegateTo),
)
import Data.Tagged (untag)
import Plutarch.Context (
SpendingBuilder,
buildSpendingUnsafe,
input,
output,
script,
signedWith,
txId,
withDatum,
withOutRef,
withSpendingOutRef,
withValue,
)
import PlutusLedgerApi.V1 (
PubKeyHash,
ScriptContext,
TxOutRef (TxOutRef),
)
import PlutusLedgerApi.V1.Value qualified as Value
import Sample.Shared (
agoraScripts,
governor,
minAda,
signer,
signer2,
stakeAssetClass,
stakeValidatorHash,
)
import Test.Specification (SpecificationTree, testValidator)
import Test.Util (pubKeyHashes, sortValue)
--------------------------------------------------------------------------------
-- | Parameters that control the script context generation of 'setDelegate'.
data Parameters = Parameters
{ existingDelegate :: Maybe PubKeyHash
-- ^ Whom the stake has been delegated to.
, newDelegate :: Maybe PubKeyHash
-- ^ The new delegate to set to.
, invalidOutputStake :: Bool
-- ^ The output stake datum will be invalid if this is set to true.
, signedByOwner :: Bool
-- ^ Whether the stake owner signs the transaction o not.
}
-- | Select the correct stake redeemer based on the existence of the new delegate.
mkStakeRedeemer :: Parameters -> StakeRedeemer
mkStakeRedeemer (newDelegate -> d) = maybe ClearDelegate DelegateTo d
-- | The owner of the input stake.
stakeOwner :: PubKeyHash
stakeOwner = signer
-- | Create input stake datum given the parameters.
mkStakeInputDatum :: Parameters -> StakeDatum
mkStakeInputDatum ps =
StakeDatum
{ stakedAmount = 5
, owner = stakeOwner
, delegatedTo = ps.existingDelegate
, lockedBy = []
}
-- | Generate a 'ScriptContext' that tries to change the delegate of a stake.
setDelegate :: Parameters -> ScriptContext
setDelegate ps = buildSpendingUnsafe builder
where
stakeRef :: TxOutRef
stakeRef = TxOutRef "0ffef57e30cc604342c738e31e0451593837b313e7bfb94b0922b142782f98e6" 1
stakeInput = mkStakeInputDatum ps
stakeOutput =
let stakedAmount =
if ps.invalidOutputStake
then stakeInput.stakedAmount - 1
else stakeInput.stakedAmount
in stakeInput
{ stakedAmount = stakedAmount
, delegatedTo = ps.newDelegate
}
signer =
if ps.signedByOwner
then stakeInput.owner
else signer2
st = Value.assetClassValue stakeAssetClass 1 -- Stake ST
stakeValue =
sortValue $
mconcat
[ st
, Value.assetClassValue
(untag governor.gtClassRef)
(untag stakeInput.stakedAmount)
, minAda
]
builder :: SpendingBuilder
builder =
mconcat
[ txId "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be"
, signedWith signer
, input $
mconcat
[ script stakeValidatorHash
, withValue stakeValue
, withDatum stakeInput
, withOutRef stakeRef
]
, output $
mconcat
[ script stakeValidatorHash
, withValue stakeValue
, withDatum stakeOutput
]
, withSpendingOutRef stakeRef
]
--------------------------------------------------------------------------------
{- | Create a test case that runs the stake validator to test the functionality
of setting the delegate.P
-}
mkTestCase :: String -> Parameters -> Bool -> SpecificationTree
mkTestCase name ps valid =
testValidator
valid
name
agoraScripts.compiledStakeValidator
(mkStakeInputDatum ps)
(mkStakeRedeemer ps)
(setDelegate ps)
--------------------------------------------------------------------------------
-- * Valid Parameters
overrideExistingDelegateParameters :: Parameters
overrideExistingDelegateParameters =
Parameters
{ existingDelegate = Just $ head pubKeyHashes
, newDelegate = Just $ pubKeyHashes !! 2
, invalidOutputStake = False
, signedByOwner = True
}
clearDelegateParameters :: Parameters
clearDelegateParameters =
overrideExistingDelegateParameters
{ newDelegate = Nothing
}
setDelegateParameters :: Parameters
setDelegateParameters =
overrideExistingDelegateParameters
{ existingDelegate = Nothing
}
--------------------------------------------------------------------------------
-- * Invalid Parameters
ownerDoesntSignParameters :: Parameters
ownerDoesntSignParameters =
overrideExistingDelegateParameters
{ signedByOwner = False
}
delegateToOwnerParameters :: Parameters
delegateToOwnerParameters =
overrideExistingDelegateParameters
{ existingDelegate = Nothing
, newDelegate = Just stakeOwner
}
invalidOutputStakeDatumParameters :: Parameters
invalidOutputStakeDatumParameters =
overrideExistingDelegateParameters
{ invalidOutputStake = True
}

View file

@ -19,7 +19,6 @@ module Sample.Treasury (
import Plutarch.Context ( import Plutarch.Context (
MintingBuilder, MintingBuilder,
UTXO,
buildMintingUnsafe, buildMintingUnsafe,
credential, credential,
input, input,
@ -28,6 +27,7 @@ import Plutarch.Context (
script, script,
signedWith, signedWith,
txId, txId,
withMinting,
withTxId, withTxId,
withValue, withValue,
) )
@ -56,17 +56,19 @@ import Sample.Shared (
baseCtxBuilder :: MintingBuilder baseCtxBuilder :: MintingBuilder
baseCtxBuilder = baseCtxBuilder =
let treasury :: UTXO -> UTXO let treasury =
treasury = mconcat
credential trCredential [ credential trCredential
. withValue minAda , withValue minAda
. withTxId "73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049" , withTxId "73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049"
]
in mconcat in mconcat
[ txId "73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049" [ txId "73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049"
, signedWith signer , signedWith signer
, mint (Value.singleton gatCs gatTn (-1)) , mint (Value.singleton gatCs gatTn (-1))
, input treasury , input treasury
, output treasury , output treasury
, withMinting gatCs
] ]
{- | A `ScriptContext` that should be compatible with treasury {- | A `ScriptContext` that should be compatible with treasury
@ -79,9 +81,11 @@ validCtx =
mconcat mconcat
[ baseCtxBuilder [ baseCtxBuilder
, input $ , input $
script mockTrEffectHash mconcat
. withValue (Value.singleton gatCs gatTn 1 <> minAda) [ script mockTrEffectHash
. withTxId "52b67b60260da3937510ad545c7f46f8d9915bd27e1082e76947fb309f913bd3" , withValue (Value.singleton gatCs gatTn 1 <> minAda)
, withTxId "52b67b60260da3937510ad545c7f46f8d9915bd27e1082e76947fb309f913bd3"
]
] ]
in buildMintingUnsafe builder in buildMintingUnsafe builder
@ -120,8 +124,10 @@ trCtxGATNameNotAddress =
mconcat mconcat
[ baseCtxBuilder [ baseCtxBuilder
, input $ , input $
script wrongEffHash mconcat
. withValue (Value.singleton gatCs gatTn 1 <> minAda) [ script wrongEffHash
. withTxId "52b67b60260da3937510ad545c7f46f8d9915bd27e1082e76947fb309f913bd3" , withValue (Value.singleton gatCs gatTn 1 <> minAda)
, withTxId "52b67b60260da3937510ad545c7f46f8d9915bd27e1082e76947fb309f913bd3"
]
] ]
in buildMintingUnsafe builder in buildMintingUnsafe builder

View file

@ -10,20 +10,20 @@ Tests for Authority token functions
module Spec.AuthorityToken (specs) where module Spec.AuthorityToken (specs) where
import Agora.AuthorityToken (singleAuthorityTokenBurned) import Agora.AuthorityToken (singleAuthorityTokenBurned)
import Plutarch (ClosedTerm, POpaque, compile, perror, popaque) import Plutarch (ClosedTerm, POpaque, perror, popaque)
import Plutarch.Extra.Compile (mustCompile)
import Plutarch.Unsafe (punsafeCoerce)
import PlutusLedgerApi.V1 ( import PlutusLedgerApi.V1 (
Address (Address), Address (Address),
Credential (PubKeyCredential, ScriptCredential), Credential (PubKeyCredential, ScriptCredential),
CurrencySymbol, CurrencySymbol,
Script, Script,
TxInInfo (TxInInfo), TxInInfo (TxInInfo),
TxInfo (..),
TxOut (TxOut), TxOut (TxOut),
TxOutRef (TxOutRef), TxOutRef (TxOutRef),
ValidatorHash (ValidatorHash), ValidatorHash (ValidatorHash),
Value, Value,
) )
import PlutusLedgerApi.V1.Interval qualified as Interval (always)
import PlutusLedgerApi.V1.Value qualified as Value ( import PlutusLedgerApi.V1.Value qualified as Value (
Value (Value), Value (Value),
singleton, singleton,
@ -36,44 +36,32 @@ import Test.Specification (
scriptSucceeds, scriptSucceeds,
) )
import Prelude ( import Prelude (
Functor (fmap),
Maybe (Nothing), Maybe (Nothing),
PBool, PBool,
Semigroup ((<>)), Semigroup ((<>)),
fmap,
pconstant, pconstant,
pconstantData,
pif, pif,
($),
) )
currencySymbol :: CurrencySymbol currencySymbol :: CurrencySymbol
currencySymbol = "deadbeef" currencySymbol = "deadbeef"
mkTxInfo :: Value -> [TxOut] -> TxInfo mkInputs :: [TxOut] -> [TxInInfo]
mkTxInfo mint outs = mkInputs = fmap (TxInInfo (TxOutRef "" 0))
TxInfo
{ txInfoInputs = fmap (TxInInfo (TxOutRef "" 0)) outs
, txInfoOutputs = []
, txInfoFee = Value.singleton "" "" 1000
, txInfoMint = mint
, txInfoDCert = []
, txInfoWdrl = []
, txInfoValidRange = Interval.always
, txInfoSignatories = []
, txInfoData = []
, txInfoId = ""
}
singleAuthorityTokenBurnedTest :: Value -> [TxOut] -> Script singleAuthorityTokenBurnedTest :: Value -> [TxOut] -> Script
singleAuthorityTokenBurnedTest mint outs = singleAuthorityTokenBurnedTest mint outs =
let actual :: ClosedTerm PBool let actual :: ClosedTerm PBool
actual = singleAuthorityTokenBurned (pconstant currencySymbol) (pconstantData (mkTxInfo mint outs)) (pconstant mint) actual = singleAuthorityTokenBurned (pconstant currencySymbol) (punsafeCoerce $ pconstant $ mkInputs outs) (pconstant mint)
s :: ClosedTerm POpaque s :: ClosedTerm POpaque
s = s =
pif pif
actual actual
(popaque (pconstant ())) (popaque (pconstant ()))
perror perror
in compile s in mustCompile s
-- | The SpecificationTree exported by this module. -- | The SpecificationTree exported by this module.
specs :: [SpecificationTree] specs :: [SpecificationTree]

View file

@ -2,8 +2,8 @@ module Spec.Effect.GovernorMutation (specs) where
import Agora.Effect.GovernorMutation (mutateGovernorValidator) import Agora.Effect.GovernorMutation (mutateGovernorValidator)
import Agora.Governor (GovernorDatum (..), GovernorRedeemer (MutateGovernor)) import Agora.Governor (GovernorDatum (..), GovernorRedeemer (MutateGovernor))
import Agora.Governor.Scripts (governorValidator)
import Agora.Proposal (ProposalId (..)) import Agora.Proposal (ProposalId (..))
import Agora.Scripts (AgoraScripts (..))
import Data.Default.Class (Default (def)) import Data.Default.Class (Default (def))
import PlutusLedgerApi.V1 (ScriptContext (ScriptContext), ScriptPurpose (Spending)) import PlutusLedgerApi.V1 (ScriptContext (ScriptContext), ScriptPurpose (Spending))
import Sample.Effect.GovernorMutation ( import Sample.Effect.GovernorMutation (
@ -14,7 +14,7 @@ import Sample.Effect.GovernorMutation (
mkEffectTxInfo, mkEffectTxInfo,
validNewGovernorDatum, validNewGovernorDatum,
) )
import Sample.Shared qualified as Shared import Sample.Shared (agoraScripts, mkEffect)
import Test.Specification ( import Test.Specification (
SpecificationTree, SpecificationTree,
effectFailsWith, effectFailsWith,
@ -32,12 +32,13 @@ specs =
"valid new governor datum" "valid new governor datum"
[ validatorSucceedsWith [ validatorSucceedsWith
"governor validator should pass" "governor validator should pass"
(governorValidator Shared.governor) agoraScripts.compiledGovernorValidator
( GovernorDatum ( GovernorDatum
def def
(ProposalId 0) (ProposalId 0)
def def
def def
3
) )
MutateGovernor MutateGovernor
( ScriptContext ( ScriptContext
@ -46,7 +47,7 @@ specs =
) )
, effectSucceedsWith , effectSucceedsWith
"effect validator should pass" "effect validator should pass"
(mutateGovernorValidator Shared.governor) (mkEffect $ mutateGovernorValidator agoraScripts)
(mkEffectDatum validNewGovernorDatum) (mkEffectDatum validNewGovernorDatum)
(ScriptContext (mkEffectTxInfo validNewGovernorDatum) (Spending effectRef)) (ScriptContext (mkEffectTxInfo validNewGovernorDatum) (Spending effectRef))
] ]
@ -54,12 +55,13 @@ specs =
"invalid new governor datum" "invalid new governor datum"
[ validatorFailsWith [ validatorFailsWith
"governor validator should fail" "governor validator should fail"
(governorValidator Shared.governor) agoraScripts.compiledGovernorValidator
( GovernorDatum ( GovernorDatum
def def
(ProposalId 0) (ProposalId 0)
def def
def def
3
) )
MutateGovernor MutateGovernor
( ScriptContext ( ScriptContext
@ -68,7 +70,7 @@ specs =
) )
, effectFailsWith , effectFailsWith
"effect validator should fail" "effect validator should fail"
(mutateGovernorValidator Shared.governor) (mkEffect $ mutateGovernorValidator agoraScripts)
(mkEffectDatum validNewGovernorDatum) (mkEffectDatum validNewGovernorDatum)
(ScriptContext (mkEffectTxInfo invalidNewGovernorDatum) (Spending effectRef)) (ScriptContext (mkEffectTxInfo invalidNewGovernorDatum) (Spending effectRef))
] ]

View file

@ -25,12 +25,14 @@ import Sample.Effect.TreasuryWithdrawal (
treasuries, treasuries,
users, users,
) )
import Sample.Shared (mkEffect)
import Test.Specification ( import Test.Specification (
SpecificationTree, SpecificationTree,
effectFailsWith, effectFailsWith,
effectSucceedsWith, effectSucceedsWith,
group, group,
) )
import Test.Util (sortValue)
specs :: [SpecificationTree] specs :: [SpecificationTree]
specs = specs =
@ -38,7 +40,7 @@ specs =
"effect" "effect"
[ effectSucceedsWith [ effectSucceedsWith
"Simple" "Simple"
(treasuryWithdrawalValidator currSymbol) (mkEffect $ treasuryWithdrawalValidator currSymbol)
datum1 datum1
( buildScriptContext ( buildScriptContext
[ inputGAT [ inputGAT
@ -50,7 +52,7 @@ specs =
) )
, effectSucceedsWith , effectSucceedsWith
"Simple with multiple treasuries " "Simple with multiple treasuries "
(treasuryWithdrawalValidator currSymbol) (mkEffect $ treasuryWithdrawalValidator currSymbol)
datum1 datum1
( buildScriptContext ( buildScriptContext
[ inputGAT [ inputGAT
@ -67,7 +69,7 @@ specs =
) )
, effectSucceedsWith , effectSucceedsWith
"Mixed Assets" "Mixed Assets"
(treasuryWithdrawalValidator currSymbol) (mkEffect $ treasuryWithdrawalValidator currSymbol)
datum2 datum2
( buildScriptContext ( buildScriptContext
[ inputGAT [ inputGAT
@ -82,7 +84,7 @@ specs =
) )
, effectFailsWith , effectFailsWith
"Pay to uknown 3rd party" "Pay to uknown 3rd party"
(treasuryWithdrawalValidator currSymbol) (mkEffect $ treasuryWithdrawalValidator currSymbol)
datum2 datum2
( buildScriptContext ( buildScriptContext
[ inputGAT [ inputGAT
@ -98,7 +100,7 @@ specs =
) )
, effectFailsWith , effectFailsWith
"Missing receiver" "Missing receiver"
(treasuryWithdrawalValidator currSymbol) (mkEffect $ treasuryWithdrawalValidator currSymbol)
datum2 datum2
( buildScriptContext ( buildScriptContext
[ inputGAT [ inputGAT
@ -113,7 +115,7 @@ specs =
) )
, effectFailsWith , effectFailsWith
"Unauthorized treasury" "Unauthorized treasury"
(treasuryWithdrawalValidator currSymbol) (mkEffect $ treasuryWithdrawalValidator currSymbol)
datum3 datum3
( buildScriptContext ( buildScriptContext
[ inputGAT [ inputGAT
@ -125,7 +127,7 @@ specs =
) )
, effectFailsWith , effectFailsWith
"Prevent transactions besides the withdrawal" "Prevent transactions besides the withdrawal"
(treasuryWithdrawalValidator currSymbol) (mkEffect $ treasuryWithdrawalValidator currSymbol)
datum3 datum3
( buildScriptContext ( buildScriptContext
[ inputGAT [ inputGAT
@ -141,8 +143,14 @@ specs =
] ]
] ]
where where
asset1 = Value.singleton "abbc12" "OrangeBottle" asset1 =
asset2 = Value.singleton "abbc12" "19721121" Value.singleton
"0d586e057e76238f8c56c0752507bfa45ae13b04f8497a311d4aaa48"
"OrangeBottle"
asset2 =
Value.singleton
"7e6aa764bceeba1f7acf47d20f1a2a85440afa2928f8ae96376f4d85"
"19721121"
datum1 = datum1 =
TreasuryWithdrawalDatum TreasuryWithdrawalDatum
[ (head users, asset1 1) [ (head users, asset1 1)
@ -155,8 +163,8 @@ specs =
] ]
datum2 = datum2 =
TreasuryWithdrawalDatum TreasuryWithdrawalDatum
[ (head users, asset2 5 <> asset1 4) [ (head users, sortValue $ asset2 5 <> asset1 4)
, (users !! 1, asset2 1 <> asset1 2) , (users !! 1, sortValue $ asset2 1 <> asset1 2)
, (users !! 2, asset1 1) , (users !! 2, asset1 1)
] ]
[ head treasuries [ head treasuries

View file

@ -13,17 +13,11 @@ TODO: Add negative test cases, see [#76](https://github.com/Liqwid-Labs/agora/is
-} -}
module Spec.Governor (specs) where module Spec.Governor (specs) where
import Agora.Governor (GovernorDatum (..), GovernorRedeemer (..)) import Sample.Governor.Initialize qualified as GST
import Agora.Governor.Scripts (governorPolicy, governorValidator) import Sample.Governor.Mutate qualified as Mutate
import Agora.Proposal (ProposalId (..))
import Data.Default.Class (Default (def))
import Sample.Governor (createProposal, mintGATs, mintGST, mutateState)
import Sample.Shared qualified as Shared
import Test.Specification ( import Test.Specification (
SpecificationTree, SpecificationTree,
group, group,
policySucceedsWith,
validatorSucceedsWith,
) )
-- | The SpecificationTree exported by this module. -- | The SpecificationTree exported by this module.
@ -31,46 +25,38 @@ specs :: [SpecificationTree]
specs = specs =
[ group [ group
"policy" "policy"
[ policySucceedsWith [ GST.mkTestCase "totally legal" GST.totallyValidParameters True
"GST minting" , group
(governorPolicy Shared.governor) "illegal"
() [ GST.mkTestCase "invalid thresholds" GST.invalidDatumThresholdsParameters False
mintGST , GST.mkTestCase
"invalid max time range width for proposal creation"
GST.invalidDatumMaxTimeRangeWidthParameters
False
, GST.mkTestCase "invalid timings" GST.invalidDatumTimingConfigParameters False
, GST.mkTestCase "no governor datum" GST.withoutGovernorDatumParameters False
, GST.mkTestCase "no witness UTXO" GST.witnessNotPresentedParameters False
, GST.mkTestCase "mint more than one GST" GST.mintMoreThanOneGSTParameters False
, GST.mkTestCase "GST has non-empty name" GST.mintGSTWithNoneEmptyNameParameters False
]
] ]
, group , group
"validator" "validator"
[ validatorSucceedsWith [ group
"proposal creation" "mutate"
(governorValidator Shared.governor) [ Mutate.mkTestCase
( GovernorDatum "legal"
def Mutate.totallyValidBundle
(ProposalId 0) (Mutate.Validity True)
def , group "illegal" $
def map
) ( \b ->
CreateProposal Mutate.mkTestCase
createProposal "(negative test)"
, validatorSucceedsWith b
"GATs minting" (Mutate.Validity False)
(governorValidator Shared.governor) )
( GovernorDatum Mutate.invalidBundles
def ]
(ProposalId 5)
def
def
)
MintGATs
mintGATs
, validatorSucceedsWith
"mutate governor state"
(governorValidator Shared.governor)
( GovernorDatum
def
(ProposalId 5)
def
def
)
MutateGovernor
mutateState
] ]
] ]

View file

@ -1,5 +1,3 @@
{-# LANGUAGE QuasiQuotes #-}
{- | {- |
Module : Spec.Proposal Module : Spec.Proposal
Maintainer : emi@haskell.fyi Maintainer : emi@haskell.fyi
@ -9,441 +7,388 @@ Tests for Proposal policy and validator
-} -}
module Spec.Proposal (specs) where module Spec.Proposal (specs) where
import Agora.Proposal ( import Sample.Proposal.Advance qualified as Advance
Proposal (..), import Sample.Proposal.Cosign qualified as Cosign
ProposalDatum (..), import Sample.Proposal.Create qualified as Create
ProposalId (ProposalId),
ProposalRedeemer (..),
ProposalStatus (..),
ProposalThresholds (..),
ProposalVotes (ProposalVotes),
ResultTag (ResultTag),
cosigners,
effects,
emptyVotesFor,
proposalId,
status,
thresholds,
votes,
)
import Agora.Proposal.Scripts (proposalPolicy, proposalValidator)
import Agora.Proposal.Time (
ProposalStartingTime (ProposalStartingTime),
)
import Agora.Stake (
ProposalLock (ProposalLock),
StakeDatum (StakeDatum),
StakeRedeemer (PermitVote, WitnessStake),
)
import Agora.Stake.Scripts (stakeValidator)
import Data.Default.Class (Default (def))
import Data.Tagged (Tagged (Tagged), untag)
import PlutusLedgerApi.V1 (ScriptContext (..), ScriptPurpose (..))
import PlutusTx.AssocMap qualified as AssocMap
import Sample.Proposal qualified as Proposal
import Sample.Proposal.UnlockStake qualified as UnlockStake import Sample.Proposal.UnlockStake qualified as UnlockStake
import Sample.Shared (signer, signer2) import Sample.Proposal.Vote qualified as Vote
import Sample.Shared qualified as Shared (proposal, stake)
import Test.Specification ( import Test.Specification (
SpecificationTree, SpecificationTree,
group, group,
policySucceedsWith,
validatorFailsWith,
validatorSucceedsWith,
) )
-- | Stake specs. -- | Stake specs.
specs :: [SpecificationTree] specs :: [SpecificationTree]
specs = specs =
[ group [ group
"policy" "policy (proposal creation)"
[ policySucceedsWith [ Create.mkTestTree
"proposalCreation" "legal"
(proposalPolicy Shared.proposal.governorSTAssetClass) Create.totallyValidParameters
() True
Proposal.proposalCreation True
True
, group
"illegal"
[ Create.mkTestTree
"invalid next proposal id"
Create.invalidOutputGovernorDatumParameters
True
False
True
, Create.mkTestTree
"use other's stake"
Create.useStakeOwnBySomeoneElseParameters
True
True
False
, Create.mkTestTree
"altered stake"
Create.invalidOutputStakeParameters
True
False
False
, Create.mkTestTree
"invalid stake locks"
Create.addInvalidLocksParameters
True
False
True
, Create.mkTestTree
"has reached maximum proposals limit"
Create.exceedMaximumProposalsParameters
True
False
True
, Create.mkTestTree
"loose time range"
Create.timeRangeNotTightParameters
True
False
True
, Create.mkTestTree
"open time range"
Create.timeRangeNotClosedParameters
True
False
True
, group "invalid proposal status" $
map
( \ps ->
Create.mkTestTree
(show ps.proposalStatus)
ps
True
False
True
)
Create.invalidProposalStatusParameters
]
] ]
, group , group
"validator" "validator"
[ group [ group
"cosignature" "cosignature"
[ validatorSucceedsWith $ let cosignerCases = [1, 5, 10]
"proposal"
(proposalValidator Shared.proposal) mkLegalGroup nCosigners =
( ProposalDatum Cosign.mkTestTree
{ proposalId = ProposalId 0 (unwords ["with", show nCosigners, "cosigners"])
, effects = (Cosign.validCosignNParameters nCosigners)
AssocMap.fromList True
[ (ResultTag 0, AssocMap.empty) legalGroup =
, (ResultTag 1, AssocMap.empty) group "legal" $
] map mkLegalGroup cosignerCases
, status = Draft
, cosigners = [signer] mkIllegalStatusNotDraftGroup nCosigners =
, thresholds = def group (unwords ["with", show nCosigners, "cosigners"]) $
, votes = map
emptyVotesFor $ ( \ps ->
AssocMap.fromList Cosign.mkTestTree
[ (ResultTag 0, AssocMap.empty) ("status: " <> show ps.proposalStatus)
, (ResultTag 1, AssocMap.empty) ps
] False
, timingConfig = def )
, startingTime = ProposalStartingTime 0 (Cosign.statusNotDraftCosignNParameters nCosigners)
} illegalStatusNotDraftGroup =
) group "proposal status not Draft" $
(Cosign [signer2]) map mkIllegalStatusNotDraftGroup cosignerCases
(ScriptContext (Proposal.cosignProposal [signer2]) (Spending Proposal.proposalRef))
, validatorSucceedsWith illegalGroup =
"stake" group
(stakeValidator Shared.stake) "illegal"
(StakeDatum (Tagged 50_000_000) signer2 []) [ Cosign.mkTestTree
WitnessStake "duplicate cosigners"
(ScriptContext (Proposal.cosignProposal [signer2]) (Spending Proposal.stakeRef)) Cosign.duplicateCosignersParameters
] False
, Cosign.mkTestTree
"altered output stake"
Cosign.invalidStakeOutputParameters
False
, illegalStatusNotDraftGroup
]
in [legalGroup, illegalGroup]
, group , group
"voting" "voting"
[ validatorSucceedsWith [ group
"proposal" "legal"
(proposalValidator Shared.proposal) [ Vote.mkTestTree "ordinary" Vote.validVoteParameters True
( ProposalDatum , Vote.mkTestTree "delegate" Vote.validVoteAsDelegateParameters True
{ proposalId = ProposalId 42 ]
, effects = -- TODO: add negative test cases
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
]
, status = VotingReady
, cosigners = [signer]
, thresholds = def
, votes =
ProposalVotes
( AssocMap.fromList
[ (ResultTag 0, 42)
, (ResultTag 1, 4242)
]
)
, timingConfig = def
, startingTime = ProposalStartingTime 0
}
)
(Vote (ResultTag 0))
( ScriptContext
( Proposal.voteOnProposal
Proposal.VotingParameters
{ Proposal.voteFor = ResultTag 0
, Proposal.voteCount = 27
}
)
(Spending Proposal.proposalRef)
)
, validatorSucceedsWith
"stake"
(stakeValidator Shared.stake)
( StakeDatum
(Tagged 27)
signer
[ ProposalLock (ResultTag 0) (ProposalId 0)
, ProposalLock (ResultTag 2) (ProposalId 1)
]
)
(PermitVote $ ProposalLock (ResultTag 0) (ProposalId 42))
( ScriptContext
( Proposal.voteOnProposal
Proposal.VotingParameters
{ Proposal.voteFor = ResultTag 0
, Proposal.voteCount = 27
}
)
(Spending Proposal.stakeRef)
)
] ]
, group , group
"advancing" "advancing"
[ group "successfully advance to next state" $ $ let possibleCosigners = [1, 5, 10]
map possibleEffects = [1, 2, 5]
( \(name, initialState) -> in do
validatorSucceedsWith cs <- possibleCosigners
name es <- possibleEffects
(proposalValidator Shared.proposal)
( ProposalDatum let groupName =
{ proposalId = ProposalId 0 unwords
, effects = [ "with"
AssocMap.fromList , show cs
[ (ResultTag 0, AssocMap.empty) , "cosigners"
, (ResultTag 1, AssocMap.empty) , "and"
] , show es
, status = initialState , "effects"
, cosigners = [signer] ]
, thresholds = def
, votes = pure $
ProposalVotes group
( AssocMap.fromList groupName
[ [ group
( ResultTag 0 "legal"
, case initialState of $ let allValid =
Draft -> 0 Advance.Validity
_ -> untag (def :: ProposalThresholds).execute + 1 { forProposalValidator = True
) , forStakeValidator = True
, (ResultTag 1, 0) , forGovernorValidator = Just True
, forAuthorityTokenPolicy = Just True
}
mkName b =
unwords
[ "from"
, show b.proposalParameters.fromStatus
, "to"
, show b.proposalParameters.toStatus
] ]
) in [ Advance.mkTestTree'
, timingConfig = def "to next state"
, startingTime = ProposalStartingTime 0 mkName
} (Advance.mkValidToNextStateBundles cs es)
) allValid
AdvanceProposal , Advance.mkTestTree'
( ScriptContext "to failed state"
( Proposal.advanceProposalSuccess mkName
Proposal.TransitionParameters (Advance.mkValidToFailedStateBundles cs es)
{ Proposal.initialProposalStatus = initialState allValid
, Proposal.proposalStartingTime = ProposalStartingTime 0
}
)
(Spending Proposal.proposalRef)
)
)
[ ("Draft -> VotringReady", Draft)
, ("VotingReady -> Locked", VotingReady)
, ("Locked -> Finished", Locked)
]
, group "successfully advance to failed state: timeout" $
map
( \(name, initialState) ->
validatorSucceedsWith
name
(proposalValidator Shared.proposal)
( ProposalDatum
{ proposalId = ProposalId 0
, effects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
] ]
, status = initialState , group
, cosigners = [signer] "illegal"
, thresholds = def [ Advance.mkTestTree'
, votes = "advance finished proposals"
ProposalVotes (const "(negative test)")
( AssocMap.fromList (Advance.mkFromFinishedBundles cs es)
[ Advance.Validity
( ResultTag 0 { forProposalValidator = False
, case initialState of , forStakeValidator = True
Draft -> 0 , forGovernorValidator = Just False
_ -> untag (def :: ProposalThresholds).vote + 1 , forAuthorityTokenPolicy = Just True
)
, (ResultTag 1, 0)
]
)
, timingConfig = def
, startingTime = ProposalStartingTime 0
}
)
AdvanceProposal
( ScriptContext
( Proposal.advanceProposalFailureTimeout
Proposal.TransitionParameters
{ Proposal.initialProposalStatus = initialState
, Proposal.proposalStartingTime = ProposalStartingTime 0
} }
) , Advance.mkTestTree
(Spending Proposal.proposalRef) "insufficient cosigns"
) (Advance.mkInsufficientCosignsBundle cs es)
) Advance.Validity
[ ("Draft -> Finished", Draft) { forProposalValidator = False
, ("VotingReady -> Finished", VotingReady) , forStakeValidator = True
, ("Locked -> Finished", Locked) , forGovernorValidator = Nothing
] , forAuthorityTokenPolicy = Nothing
, validatorFailsWith }
"illegal: insufficient votes" , Advance.mkTestTree
(proposalValidator Shared.proposal) "insufficient votes"
( ProposalDatum (Advance.mkInsufficientVotesBundle cs es)
{ proposalId = ProposalId 0 Advance.Validity
, effects = { forProposalValidator = False
AssocMap.fromList , forStakeValidator = True
[ (ResultTag 0, AssocMap.empty) , forGovernorValidator = Nothing
, (ResultTag 1, AssocMap.empty) , forAuthorityTokenPolicy = Nothing
] }
, status = VotingReady , Advance.mkTestTree
, cosigners = [signer] "ambiguous winning effect"
, thresholds = def (Advance.mkAmbiguousWinnerBundle cs es)
, votes = Advance.Validity
ProposalVotes { forProposalValidator = False
( AssocMap.fromList , forStakeValidator = True
[ (ResultTag 0, 1) , forGovernorValidator = Nothing
, (ResultTag 1, 0) , forAuthorityTokenPolicy = Nothing
] }
, Advance.mkTestTree'
"to next state too late"
(\b -> unwords ["from", show b.proposalParameters.fromStatus])
(Advance.mkToNextStateTooLateBundles cs es)
Advance.Validity
{ forProposalValidator = False
, forStakeValidator = True
, forGovernorValidator = Just True
, forAuthorityTokenPolicy = Just True
}
, Advance.mkTestTree'
"altered output stake datum"
(\b -> unwords ["from", show b.proposalParameters.fromStatus])
(Advance.mkInvalidOutputStakeBundles cs es)
Advance.Validity
{ forProposalValidator = False
, forStakeValidator = False
, forGovernorValidator = Just True
, forAuthorityTokenPolicy = Just True
}
, Advance.mkTestTree
"forget to mint GATs"
(Advance.mkNoGATMintedBundle cs es)
Advance.Validity
{ forProposalValidator = True
, forStakeValidator = True
, forGovernorValidator = Just False
, forAuthorityTokenPolicy = Nothing
}
, Advance.mkTestTree
"mint GATs for wrong validators"
(Advance.mkMintGATsForWrongEffectsBundle cs es)
Advance.Validity
{ forProposalValidator = True
, forStakeValidator = True
, forGovernorValidator = Just False
, forAuthorityTokenPolicy = Just True
}
, Advance.mkTestTree
"mint GATs with bad token name"
(Advance.mkMintGATsWithoutTagBundle cs es)
Advance.Validity
{ forProposalValidator = True
, forStakeValidator = True
, forGovernorValidator = Just False
, forAuthorityTokenPolicy = Just False
}
, Advance.mkTestTree
"wrong GAT datum"
(Advance.mkGATsWithWrongDatumBundle cs es)
Advance.Validity
{ forProposalValidator = True
, forStakeValidator = True
, forGovernorValidator = Just False
, forAuthorityTokenPolicy = Just True
}
, Advance.mkTestTree
"invalid governor output datum"
(Advance.mkBadGovernorOutputDatumBundle cs es)
Advance.Validity
{ forProposalValidator = True
, forStakeValidator = True
, forGovernorValidator = Just False
, forAuthorityTokenPolicy = Just True
}
]
]
, group "unlocking" $
let proposalCountCases = [1, 5, 10, 42]
mkSubgroupName nProposals = unwords ["with", show nProposals, "proposals"]
mkLegalGroup nProposals =
group
(mkSubgroupName nProposals)
[ UnlockStake.mkTestTree
"voter: retract votes while voting"
(UnlockStake.mkVoterRetractVotesWhileVotingParameters nProposals)
True
, UnlockStake.mkTestTree
"voter/creator: retract votes while voting"
(UnlockStake.mkVoterCreatorRetractVotesWhileVotingParameters nProposals)
True
, UnlockStake.mkTestTree
"creator: remove creator locks when finished"
(UnlockStake.mkCreatorRemoveCreatorLocksWhenFinishedParameters nProposals)
True
, UnlockStake.mkTestTree
"voter/creator: remove all locks when finished"
(UnlockStake.mkVoterCreatorRemoveAllLocksWhenFinishedParameters nProposals)
True
, group "voter: unlock after voting" $
map
( \ps ->
let name = show ps.proposalStatus
in UnlockStake.mkTestTree name ps True
) )
, timingConfig = def (UnlockStake.mkVoterUnlockStakeAfterVotingParameters nProposals)
, startingTime = ProposalStartingTime 0 , UnlockStake.mkTestTree
} "voter/creator: remove vote locks when locked"
) (UnlockStake.mkVoterCreatorRemoveVoteLocksWhenLockedParameters nProposals)
AdvanceProposal True
( ScriptContext
Proposal.advanceProposalInsufficientVotes
(Spending Proposal.proposalRef)
)
, validatorFailsWith
"illegal: initial state is Finished"
(proposalValidator Shared.proposal)
( ProposalDatum
{ proposalId = ProposalId 0
, effects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
]
, status = Finished
, cosigners = [signer]
, thresholds = def
, votes =
ProposalVotes
( AssocMap.fromList
[ (ResultTag 0, untag (def :: ProposalThresholds).vote + 1)
, (ResultTag 1, 0)
]
)
, timingConfig = def
, startingTime = ProposalStartingTime 0
}
)
AdvanceProposal
( ScriptContext
Proposal.advanceFinishedProposal
(Spending Proposal.proposalRef)
)
, validatorFailsWith
"illegal: with stake input"
(proposalValidator Shared.proposal)
( ProposalDatum
{ proposalId = ProposalId 0
, effects =
AssocMap.fromList
[ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty)
]
, status = VotingReady
, cosigners = [signer]
, thresholds = def
, votes =
ProposalVotes
( AssocMap.fromList
[ (ResultTag 0, 0)
, (ResultTag 1, 0)
]
)
, timingConfig = def
, startingTime = ProposalStartingTime 0
}
)
AdvanceProposal
( ScriptContext
Proposal.advanceProposalWithInvalidOutputStake
(Spending Proposal.proposalRef)
)
]
, group "unlocking" $ do
proposalCount <- [1, 42]
let legalGroup = group "legal" $ do
let voterRetractVotesAndUnlockStakeWhileVoting =
UnlockStake.mkProposalValidatorTestCase
UnlockStake.UnlockStakeParameters
{ UnlockStake.proposalCount = proposalCount
, UnlockStake.stakeUsage = UnlockStake.Voter
, UnlockStake.retractVotes = True
, UnlockStake.proposalStatus = VotingReady
}
True
creatorUnlockStakeWhileFinished =
UnlockStake.mkProposalValidatorTestCase
UnlockStake.UnlockStakeParameters
{ UnlockStake.proposalCount = proposalCount
, UnlockStake.stakeUsage = UnlockStake.Creator
, UnlockStake.retractVotes = False
, UnlockStake.proposalStatus = Finished
}
True
let voterUnlockStakeAfterVoting = group "voter unlocks stake after voting" $ do
status <- [Finished, Locked]
pure $
UnlockStake.mkProposalValidatorTestCase
UnlockStake.UnlockStakeParameters
{ UnlockStake.proposalCount = proposalCount
, UnlockStake.stakeUsage = UnlockStake.Voter
, UnlockStake.retractVotes = False
, UnlockStake.proposalStatus = status
}
True
[ voterRetractVotesAndUnlockStakeWhileVoting
, creatorUnlockStakeWhileFinished
, voterUnlockStakeAfterVoting
] ]
let illegalGroup = group "illegal" $ do mkIllegalGroup nProposals =
let retractsVotesWhileNotVotingReady = group
group "voter retracts votes while not voting" $ do (mkSubgroupName nProposals)
status <- [Draft, Locked, Finished] [ group "retract votes while not voting" $
map
pure $ ( \ps ->
UnlockStake.mkProposalValidatorTestCase let name =
UnlockStake.UnlockStakeParameters unwords
{ UnlockStake.proposalCount = proposalCount [ "role:"
, UnlockStake.stakeUsage = UnlockStake.Voter , show ps.stakeRole
, UnlockStake.retractVotes = True , ","
, UnlockStake.proposalStatus = status , "status:"
} , show ps.proposalStatus
False ]
in UnlockStake.mkTestTree name ps False
unlockIrrelevantStake = )
group "unlock an irrelevant stake" $ do (UnlockStake.mkRetractVotesWhileNotVoting nProposals)
status <- [Draft, VotingReady, Locked, Finished] , group "unlock an irrelevant stake" $
shouldRetractVotes <- [True, False] map
( \ps ->
pure $ let name =
UnlockStake.mkProposalValidatorTestCase unwords
UnlockStake.UnlockStakeParameters [ "status:"
{ UnlockStake.proposalCount = proposalCount , show ps.proposalStatus
, UnlockStake.stakeUsage = UnlockStake.Irrelevant , "retract votes:"
, UnlockStake.retractVotes = shouldRetractVotes , show ps.retractVotes
, UnlockStake.proposalStatus = status ]
} in UnlockStake.mkTestTree name ps False
False )
(UnlockStake.mkUnockIrrelevantStakeParameters nProposals)
unlockCreatorStakeBeforeFinished = , group "remove creator too early" $
group "unlock creator stake before finished" $ do map
status <- [Draft, VotingReady, Locked] ( \ps ->
let name =
pure $ unwords
UnlockStake.mkProposalValidatorTestCase ["status:", show ps.proposalStatus]
UnlockStake.UnlockStakeParameters in UnlockStake.mkTestTree name ps False
{ UnlockStake.proposalCount = proposalCount )
, UnlockStake.stakeUsage = UnlockStake.Creator (UnlockStake.mkRemoveCreatorLockBeforeFinishedParameters nProposals)
, UnlockStake.retractVotes = False , UnlockStake.mkTestTree
, UnlockStake.proposalStatus = status "creator: retract votes"
} (UnlockStake.mkRetractVotesWithCreatorStakeParamaters nProposals)
False False
retractVotesWithCreatorStake = , group "alter output stake datum" $
group "creator stake retracts votes" $ do map
status <- [Draft, VotingReady, Locked, Finished] ( \ps ->
let name =
pure $ unwords
UnlockStake.mkProposalValidatorTestCase [ "role:"
UnlockStake.UnlockStakeParameters , show ps.stakeRole
{ UnlockStake.proposalCount = proposalCount , ","
, UnlockStake.stakeUsage = UnlockStake.Creator , "status:"
, UnlockStake.retractVotes = True , show ps.proposalStatus
, UnlockStake.proposalStatus = status ]
} in UnlockStake.mkTestTree name ps False
False )
(UnlockStake.mkAlterStakeParameters nProposals)
[ retractsVotesWhileNotVotingReady
, unlockIrrelevantStake
, unlockCreatorStakeBeforeFinished
, retractVotesWithCreatorStake
] ]
[legalGroup, illegalGroup] legalGroup = group "legal" $ map mkLegalGroup proposalCountCases
illegalGroup = group "illegal" $ map mkIllegalGroup proposalCountCases
in [legalGroup, illegalGroup]
] ]
] ]

View file

@ -9,12 +9,14 @@ Tests for Stake policy and validator
-} -}
module Spec.Stake (specs) where module Spec.Stake (specs) where
import Agora.Scripts (AgoraScripts (..))
import Agora.Stake ( import Agora.Stake (
Stake (..),
StakeDatum (StakeDatum), StakeDatum (StakeDatum),
StakeRedeemer (DepositWithdraw), StakeRedeemer (DepositWithdraw),
) )
import Agora.Stake.Scripts (stakePolicy, stakeValidator) import Data.Bool (Bool (..))
import Data.Maybe (Maybe (..))
import Sample.Shared (agoraScripts)
import Sample.Stake ( import Sample.Stake (
DepositWithdrawExample ( DepositWithdrawExample (
DepositWithdrawExample, DepositWithdrawExample,
@ -24,12 +26,12 @@ import Sample.Stake (
signer, signer,
) )
import Sample.Stake qualified as Stake ( import Sample.Stake qualified as Stake (
stake,
stakeCreation, stakeCreation,
stakeCreationUnsigned, stakeCreationUnsigned,
stakeCreationWrongDatum, stakeCreationWrongDatum,
stakeDepositWithdraw, stakeDepositWithdraw,
) )
import Sample.Stake.SetDelegate qualified as SetDelegate
import Test.Specification ( import Test.Specification (
SpecificationTree, SpecificationTree,
group, group,
@ -38,7 +40,6 @@ import Test.Specification (
validatorFailsWith, validatorFailsWith,
validatorSucceedsWith, validatorSucceedsWith,
) )
import Test.Util (toDatum)
import Prelude (Num (negate), ($)) import Prelude (Num (negate), ($))
-- | The SpecificationTree exported by this module. -- | The SpecificationTree exported by this module.
@ -48,17 +49,17 @@ specs =
"policy" "policy"
[ policySucceedsWith [ policySucceedsWith
"stakeCreation" "stakeCreation"
(stakePolicy Stake.stake.gtClassRef) agoraScripts.compiledStakePolicy
() ()
Stake.stakeCreation Stake.stakeCreation
, policyFailsWith , policyFailsWith
"stakeCreationWrongDatum" "stakeCreationWrongDatum"
(stakePolicy Stake.stake.gtClassRef) agoraScripts.compiledStakePolicy
() ()
Stake.stakeCreationWrongDatum Stake.stakeCreationWrongDatum
, policyFailsWith , policyFailsWith
"stakeCreationUnsigned" "stakeCreationUnsigned"
(stakePolicy Stake.stake.gtClassRef) agoraScripts.compiledStakePolicy
() ()
Stake.stakeCreationUnsigned Stake.stakeCreationUnsigned
] ]
@ -66,21 +67,48 @@ specs =
"validator" "validator"
[ validatorSucceedsWith [ validatorSucceedsWith
"stakeDepositWithdraw deposit" "stakeDepositWithdraw deposit"
(stakeValidator Stake.stake) agoraScripts.compiledStakeValidator
(toDatum $ StakeDatum 100_000 signer []) (StakeDatum 100_000 signer Nothing [])
(toDatum $ DepositWithdraw 100_000) (DepositWithdraw 100_000)
(Stake.stakeDepositWithdraw $ DepositWithdrawExample {startAmount = 100_000, delta = 100_000}) (Stake.stakeDepositWithdraw $ DepositWithdrawExample {startAmount = 100_000, delta = 100_000})
, validatorSucceedsWith , validatorSucceedsWith
"stakeDepositWithdraw withdraw" "stakeDepositWithdraw withdraw"
(stakeValidator Stake.stake) agoraScripts.compiledStakeValidator
(toDatum $ StakeDatum 100_000 signer []) (StakeDatum 100_000 signer Nothing [])
(toDatum $ DepositWithdraw $ negate 100_000) (DepositWithdraw $ negate 100_000)
(Stake.stakeDepositWithdraw $ DepositWithdrawExample {startAmount = 100_000, delta = negate 100_000}) (Stake.stakeDepositWithdraw $ DepositWithdrawExample {startAmount = 100_000, delta = negate 100_000})
, validatorFailsWith , validatorFailsWith
"stakeDepositWithdraw negative GT" "stakeDepositWithdraw negative GT"
(stakeValidator Stake.stake) agoraScripts.compiledStakeValidator
(toDatum $ StakeDatum 100_000 signer []) (StakeDatum 100_000 signer Nothing [])
(toDatum $ DepositWithdraw 1_000_000) (DepositWithdraw 1_000_000)
(Stake.stakeDepositWithdraw $ DepositWithdrawExample {startAmount = 100_000, delta = negate 1_000_000}) (Stake.stakeDepositWithdraw $ DepositWithdrawExample {startAmount = 100_000, delta = negate 1_000_000})
, group
"set delegate"
[ SetDelegate.mkTestCase
"override existing delegate"
SetDelegate.overrideExistingDelegateParameters
True
, SetDelegate.mkTestCase
"remove existing delegate"
SetDelegate.clearDelegateParameters
True
, SetDelegate.mkTestCase
"set delegate to something"
SetDelegate.setDelegateParameters
True
, SetDelegate.mkTestCase
"owner doesn't sign the transaction"
SetDelegate.ownerDoesntSignParameters
False
, SetDelegate.mkTestCase
"delegate to the owner"
SetDelegate.delegateToOwnerParameters
False
, SetDelegate.mkTestCase
"invalid output stake"
SetDelegate.invalidOutputStakeDatumParameters
False
]
] ]
] ]

View file

@ -25,6 +25,8 @@ import Agora.Treasury (
TreasuryRedeemer (SpendTreasuryGAT), TreasuryRedeemer (SpendTreasuryGAT),
treasuryValidator, treasuryValidator,
) )
import Agora.Utils (CompiledValidator (CompiledValidator))
import Plutarch.Api.V1 (mkValidator)
import PlutusLedgerApi.V1 (DCert (DCertDelegRegKey)) import PlutusLedgerApi.V1 (DCert (DCertDelegRegKey))
import PlutusLedgerApi.V1.Contexts ( import PlutusLedgerApi.V1.Contexts (
ScriptContext (scriptContextPurpose, scriptContextTxInfo), ScriptContext (scriptContextPurpose, scriptContextTxInfo),
@ -35,7 +37,7 @@ import PlutusLedgerApi.V1.Credential (
StakingCredential (StakingHash), StakingCredential (StakingHash),
) )
import PlutusLedgerApi.V1.Value qualified as Value (singleton) import PlutusLedgerApi.V1.Value qualified as Value (singleton)
import Sample.Shared (trCredential) import Sample.Shared (deterministicTracingConfing, trCredential)
import Sample.Treasury ( import Sample.Treasury (
gatCs, gatCs,
gatTn, gatTn,
@ -51,6 +53,12 @@ import Test.Specification (
validatorSucceedsWith, validatorSucceedsWith,
) )
compiledTreasuryValidator :: CompiledValidator () TreasuryRedeemer
compiledTreasuryValidator =
CompiledValidator $
mkValidator deterministicTracingConfing $
treasuryValidator gatCs
specs :: [SpecificationTree] specs :: [SpecificationTree]
specs = specs =
[ group [ group
@ -59,7 +67,7 @@ specs =
"Positive" "Positive"
[ validatorSucceedsWith [ validatorSucceedsWith
"Allows for effect changes" "Allows for effect changes"
(treasuryValidator gatCs) compiledTreasuryValidator
() ()
SpendTreasuryGAT SpendTreasuryGAT
validCtx validCtx
@ -70,7 +78,7 @@ specs =
"Fails with ScriptPurpose not Minting" "Fails with ScriptPurpose not Minting"
[ validatorFailsWith [ validatorFailsWith
"Spending" "Spending"
(treasuryValidator gatCs) compiledTreasuryValidator
() ()
SpendTreasuryGAT SpendTreasuryGAT
validCtx validCtx
@ -78,7 +86,7 @@ specs =
} }
, validatorFailsWith , validatorFailsWith
"Rewarding" "Rewarding"
(treasuryValidator gatCs) compiledTreasuryValidator
() ()
SpendTreasuryGAT SpendTreasuryGAT
validCtx validCtx
@ -88,7 +96,7 @@ specs =
} }
, validatorFailsWith , validatorFailsWith
"Certifying" "Certifying"
(treasuryValidator gatCs) compiledTreasuryValidator
() ()
SpendTreasuryGAT SpendTreasuryGAT
validCtx validCtx
@ -100,7 +108,7 @@ specs =
] ]
, validatorFailsWith -- TODO: Use QuickCheck. , validatorFailsWith -- TODO: Use QuickCheck.
"Fails when multiple GATs burned" "Fails when multiple GATs burned"
(treasuryValidator gatCs) compiledTreasuryValidator
() ()
SpendTreasuryGAT SpendTreasuryGAT
validCtx validCtx
@ -115,13 +123,13 @@ specs =
} }
, validatorFailsWith , validatorFailsWith
"Fails when GAT token name is not script address" "Fails when GAT token name is not script address"
(treasuryValidator gatCs) compiledTreasuryValidator
() ()
SpendTreasuryGAT SpendTreasuryGAT
trCtxGATNameNotAddress trCtxGATNameNotAddress
, validatorFailsWith , validatorFailsWith
"Fails with wallet as input" "Fails with wallet as input"
(treasuryValidator gatCs) compiledTreasuryValidator
() ()
SpendTreasuryGAT SpendTreasuryGAT
( let txInfo = validCtx.scriptContextTxInfo ( let txInfo = validCtx.scriptContextTxInfo

View file

@ -8,7 +8,6 @@ import Test.Tasty (defaultMain, testGroup)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Property.Governor qualified as Governer import Property.Governor qualified as Governer
import Property.MultiSig qualified as MultiSig
import Spec.AuthorityToken qualified as AuthorityToken import Spec.AuthorityToken qualified as AuthorityToken
import Spec.Effect.GovernorMutation qualified as GovernorMutation import Spec.Effect.GovernorMutation qualified as GovernorMutation
import Spec.Effect.TreasuryWithdrawal qualified as TreasuryWithdrawal import Spec.Effect.TreasuryWithdrawal qualified as TreasuryWithdrawal
@ -42,7 +41,4 @@ main = do
, testGroup , testGroup
"Utility tests" "Utility tests"
Utils.tests Utils.tests
, testGroup
"Multisig tests"
MultiSig.props
] ]

View file

@ -42,16 +42,25 @@ module Test.Specification (
validatorFailsWith, validatorFailsWith,
effectSucceedsWith, effectSucceedsWith,
effectFailsWith, effectFailsWith,
testValidator,
testPolicy,
-- * Converters -- * Converters
toTestTree, toTestTree,
) where ) where
import Plutarch.Api.V1 (PMintingPolicy, PValidator) import Agora.Utils (CompiledEffect (..), CompiledMintingPolicy (..), CompiledValidator (..))
import Plutarch.Builtin (pforgetData) import Control.Composition ((.**), (.***))
import Data.Coerce (coerce)
import Plutarch.Evaluate (evalScript) import Plutarch.Evaluate (evalScript)
import Plutarch.Lift (PUnsafeLiftDecl (PLifted)) import PlutusLedgerApi.V1 (
import PlutusLedgerApi.V1 (Script, ScriptContext) Datum (..),
Redeemer (Redeemer),
Script,
ScriptContext,
ToData (toBuiltinData),
)
import PlutusLedgerApi.V1.Scripts (Context (..), applyMintingPolicyScript, applyValidator)
import PlutusTx.IsData qualified as PlutusTx (ToData) import PlutusTx.IsData qualified as PlutusTx (ToData)
import Test.Tasty (TestTree, testGroup) import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (assertFailure, testCase) import Test.Tasty.HUnit (assertFailure, testCase)
@ -150,106 +159,152 @@ scriptSucceeds name script = Terminal $ Specification name Success script
scriptFails :: String -> Script -> SpecificationTree scriptFails :: String -> Script -> SpecificationTree
scriptFails name script = Terminal $ Specification name Failure script scriptFails name script = Terminal $ Specification name Failure script
mkContext :: ScriptContext -> Context
mkContext = Context . toBuiltinData
mkRedeemer ::
forall redeemer.
(PlutusTx.ToData redeemer) =>
redeemer ->
Redeemer
mkRedeemer = Redeemer . toBuiltinData
mkDatum ::
forall datum.
(PlutusTx.ToData datum) =>
datum ->
Datum
mkDatum = Datum . toBuiltinData
applyMintingPolicy' ::
(PlutusTx.ToData redeemer) =>
CompiledMintingPolicy redeemer ->
redeemer ->
ScriptContext ->
Script
applyMintingPolicy' policy redeemer scriptContext =
applyMintingPolicyScript
(mkContext scriptContext)
(getCompiledMintingPolicy policy)
(mkRedeemer redeemer)
applyValidator' ::
( PlutusTx.ToData datum
, PlutusTx.ToData redeemer
) =>
CompiledValidator datum redeemer ->
datum ->
redeemer ->
ScriptContext ->
Script
applyValidator' validator datum redeemer scriptContext =
applyValidator
(mkContext scriptContext)
(getCompiledValidator validator)
(mkDatum datum)
(mkRedeemer redeemer)
-- | Check that a policy script succeeds, given a name and arguments. -- | Check that a policy script succeeds, given a name and arguments.
policySucceedsWith :: policySucceedsWith ::
( PLift redeemer (PlutusTx.ToData redeemer) =>
, PlutusTx.ToData (PLifted redeemer)
) =>
String -> String ->
ClosedTerm PMintingPolicy -> CompiledMintingPolicy redeemer ->
PLifted redeemer -> redeemer ->
ScriptContext -> ScriptContext ->
SpecificationTree SpecificationTree
policySucceedsWith tag policy redeemer scriptContext = policySucceedsWith tag =
scriptSucceeds tag $ scriptSucceeds tag .** applyMintingPolicy'
compile
( policy
# pforgetData (pconstantData redeemer)
# pconstant scriptContext
)
-- | Check that a policy script fails, given a name and arguments. -- | Check that a policy script fails, given a name and arguments.
policyFailsWith :: policyFailsWith ::
( PLift redeemer (PlutusTx.ToData redeemer) =>
, PlutusTx.ToData (PLifted redeemer)
) =>
String -> String ->
ClosedTerm PMintingPolicy -> CompiledMintingPolicy redeemer ->
PLifted redeemer -> redeemer ->
ScriptContext -> ScriptContext ->
SpecificationTree SpecificationTree
policyFailsWith tag policy redeemer scriptContext = policyFailsWith tag =
scriptFails tag $ scriptFails tag .** applyMintingPolicy'
compile
( policy
# pforgetData (pconstantData redeemer)
# pconstant scriptContext
)
-- | Check that a validator script succeeds, given a name and arguments. -- | Check that a validator script succeeds, given a name and arguments.
validatorSucceedsWith :: validatorSucceedsWith ::
( PLift datum ( PlutusTx.ToData datum
, PlutusTx.ToData (PLifted datum) , PlutusTx.ToData redeemer
, PLift redeemer
, PlutusTx.ToData (PLifted redeemer)
) => ) =>
String -> String ->
ClosedTerm PValidator -> CompiledValidator datum redeemer ->
PLifted datum -> datum ->
PLifted redeemer -> redeemer ->
ScriptContext -> ScriptContext ->
SpecificationTree SpecificationTree
validatorSucceedsWith tag validator datum redeemer scriptContext = validatorSucceedsWith tag =
scriptSucceeds tag $ scriptSucceeds tag .*** applyValidator'
compile
( validator
# pforgetData (pconstantData datum)
# pforgetData (pconstantData redeemer)
# pconstant scriptContext
)
-- | Check that a validator script fails, given a name and arguments. -- | Check that a validator script fails, given a name and arguments.
validatorFailsWith :: validatorFailsWith ::
( PLift datum ( PlutusTx.ToData datum
, PlutusTx.ToData (PLifted datum) , PlutusTx.ToData redeemer
, PLift redeemer
, PlutusTx.ToData (PLifted redeemer)
) => ) =>
String -> String ->
ClosedTerm PValidator -> CompiledValidator datum redeemer ->
PLifted datum -> datum ->
PLifted redeemer -> redeemer ->
ScriptContext -> ScriptContext ->
SpecificationTree SpecificationTree
validatorFailsWith tag validator datum redeemer scriptContext = validatorFailsWith tag =
scriptFails tag $ scriptFails tag .*** applyValidator'
compile
( validator
# pforgetData (pconstantData datum)
# pforgetData (pconstantData redeemer)
# pconstant scriptContext
)
-- | Check that an effect succeeds, given a name and argument. -- | Check that an effect succeeds, given a name and argument.
effectSucceedsWith :: effectSucceedsWith ::
( PLift datum ( PlutusTx.ToData datum
, PlutusTx.ToData (PLifted datum)
) => ) =>
String -> String ->
ClosedTerm PValidator -> CompiledEffect datum ->
PLifted datum -> datum ->
ScriptContext -> ScriptContext ->
SpecificationTree SpecificationTree
effectSucceedsWith tag eff datum = validatorSucceedsWith tag eff datum () effectSucceedsWith tag eff datum = validatorSucceedsWith tag (coerce eff) datum ()
-- | Check that an effect fails, given a name and argument. -- | Check that an effect fails, given a name and argument.
effectFailsWith :: effectFailsWith ::
( PLift datum ( PlutusTx.ToData datum
, PlutusTx.ToData (PLifted datum)
) => ) =>
String -> String ->
ClosedTerm PValidator -> CompiledEffect datum ->
PLifted datum -> datum ->
ScriptContext -> ScriptContext ->
SpecificationTree SpecificationTree
effectFailsWith tag eff datum = validatorFailsWith tag eff datum () effectFailsWith tag eff datum = validatorFailsWith tag (coerce eff) datum ()
-- | Test a validator, given the expectation as a boolean value.
testValidator ::
forall datum redeemer.
(PlutusTx.ToData datum, PlutusTx.ToData redeemer) =>
-- | Is this test case expected to succeed?
Bool ->
String ->
CompiledValidator datum redeemer ->
datum ->
redeemer ->
ScriptContext ->
SpecificationTree
testValidator isValid =
if isValid
then validatorSucceedsWith
else validatorFailsWith
-- | Test a policy, given the expectation as a boolean value.
testPolicy ::
forall redeemer.
(PlutusTx.ToData redeemer) =>
-- | Is this test case expected to succeed?
Bool ->
String ->
CompiledMintingPolicy redeemer ->
redeemer ->
ScriptContext ->
SpecificationTree
testPolicy isValid =
if isValid
then policySucceedsWith
else policyFailsWith

View file

@ -13,22 +13,46 @@ module Test.Util (
updateMap, updateMap,
sortMap, sortMap,
sortValue, sortValue,
blake2b_224,
pubKeyHashes,
userCredentials,
scriptCredentials,
validatorHashes,
groupsOfN,
mkSpending,
mkMinting,
CombinableBuilder,
) where ) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Prelude
--------------------------------------------------------------------------------
import Codec.Serialise (serialise) import Codec.Serialise (serialise)
import Data.ByteString.Lazy qualified as ByteString.Lazy import Crypto.Hash qualified as Crypto
--------------------------------------------------------------------------------
import Data.Bifunctor (second) import Data.Bifunctor (second)
import Data.ByteArray qualified as BA
import Data.ByteString qualified as BS
import Data.ByteString.Char8 qualified as C
import Data.ByteString.Lazy qualified as ByteString.Lazy
import Data.List (sortOn) import Data.List (sortOn)
import Plutarch.Context (
Builder,
buildMintingUnsafe,
buildSpendingUnsafe,
withMinting,
withSpendingOutRef,
)
import Plutarch.Crypto (pblake2b_256) import Plutarch.Crypto (pblake2b_256)
import PlutusLedgerApi.V1 (
Credential (
PubKeyCredential,
ScriptCredential
),
CurrencySymbol,
PubKeyHash (..),
ScriptContext,
TxOutRef,
ValidatorHash (ValidatorHash),
)
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.Scripts (Datum (Datum), DatumHash (DatumHash))
import PlutusLedgerApi.V1.Value (Value (..)) import PlutusLedgerApi.V1.Value (Value (..))
@ -36,6 +60,7 @@ import PlutusTx.AssocMap qualified as AssocMap
import PlutusTx.Builtins qualified as PlutusTx import PlutusTx.Builtins qualified as PlutusTx
import PlutusTx.IsData qualified as PlutusTx import PlutusTx.IsData qualified as PlutusTx
import PlutusTx.Ord qualified as PlutusTx import PlutusTx.Ord qualified as PlutusTx
import Prelude
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -92,12 +117,16 @@ updateMap f k =
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | Sort the given 'AssocMap.Map' by keys in ascending order.
sortMap :: forall k v. Ord k => AssocMap.Map k v -> AssocMap.Map k v sortMap :: forall k v. Ord k => AssocMap.Map k v -> AssocMap.Map k v
sortMap = sortMap =
AssocMap.fromList AssocMap.fromList
. sortOn fst . sortOn fst
. AssocMap.toList . AssocMap.toList
{- | Sort the given 'Value' in ascending order. Some plutarch functions that
work with plutarch's 'Sorted' 'PMap' require this to work correctly.
-}
sortValue :: Value -> Value sortValue :: Value -> Value
sortValue = sortValue =
Value Value
@ -106,3 +135,75 @@ sortValue =
. fmap (second sortMap) . fmap (second sortMap)
. AssocMap.toList . AssocMap.toList
. getValue . getValue
--------------------------------------------------------------------------------
-- | Compute the hash of a given byte string using blake2b_224 algorithm.
blake2b_224 :: BS.ByteString -> BS.ByteString
blake2b_224 = BS.pack . BA.unpack . Crypto.hashWith Crypto.Blake2b_224
-- | An infinite list of blake2b_224 hashes.
blake2b_224Hashes :: [BS.ByteString]
blake2b_224Hashes = blake2b_224 . C.pack . show @Integer <$> [0 ..]
-- | An infinite list of *valid* 'PubKeyHash'.
pubKeyHashes :: [PubKeyHash]
pubKeyHashes = PubKeyHash . PlutusTx.toBuiltin <$> blake2b_224Hashes
-- | An infinite list of *valid* user credentials.
userCredentials :: [Credential]
userCredentials = PubKeyCredential <$> pubKeyHashes
-- | An infinite list of *valid* validator hashes.
validatorHashes :: [ValidatorHash]
validatorHashes = ValidatorHash . PlutusTx.toBuiltin <$> blake2b_224Hashes
-- | An infinite list of *valid* script credentials.
scriptCredentials :: [Credential]
scriptCredentials = ScriptCredential <$> validatorHashes
--------------------------------------------------------------------------------
-- | Turn the given list in to groups which have the given length.
groupsOfN :: Int -> [a] -> [[a]]
groupsOfN _ [] = []
groupsOfN n xs =
let (nextGroup, rest) = next n xs
in nextGroup : groupsOfN n rest
where
next :: Int -> [a] -> ([a], [a])
next _ [] = ([], [])
next 0 xs = ([], xs)
next n (x : xs) =
let (xs', rest) = next (n - 1) xs
in (x : xs', rest)
--------------------------------------------------------------------------------
{- | Given the builder generator and the parameters, create a 'ScriptContext'
that spends the UTXO that referenced by the given 'TxOutRef'.
-}
mkSpending ::
forall ps.
(forall b. (Monoid b, Builder b) => ps -> b) ->
ps ->
TxOutRef ->
ScriptContext
mkSpending mkBuilder ps oref =
buildSpendingUnsafe $
mkBuilder ps <> withSpendingOutRef oref
{- | Given the builder generator and the parameters, create a 'ScriptContext'
that mints the token of the given currency symbol.
-}
mkMinting ::
forall ps.
(forall b. (Monoid b, Builder b) => ps -> b) ->
ps ->
CurrencySymbol ->
ScriptContext
mkMinting mkBuilder ps cs =
buildMintingUnsafe $
mkBuilder ps <> withMinting cs
type CombinableBuilder b = (Monoid b, Builder b)

View file

@ -1,6 +1,6 @@
cabal-version: 3.0 cabal-version: 3.0
name: agora name: agora
version: 0.1.0 version: 0.2.0
extra-source-files: CHANGELOG.md extra-source-files: CHANGELOG.md
author: Emily Martins <emi@haskell.fyi> author: Emily Martins <emi@haskell.fyi>
license: Apache-2.0 license: Apache-2.0
@ -12,11 +12,11 @@ license: Apache-2.0
common lang common lang
ghc-options: ghc-options:
-Wall -Wcompat -Wincomplete-uni-patterns -Wno-unused-do-bind -Werror -Wall -Wcompat -Wincomplete-uni-patterns
-Wno-partial-type-signatures -Wmissing-export-lists -Wno-unused-do-bind -Wno-partial-type-signatures
-Wincomplete-record-updates -Wmissing-deriving-strategies -Wmissing-export-lists -Wincomplete-record-updates
-Wno-name-shadowing -Wunused-foralls -fprint-explicit-foralls -Wmissing-deriving-strategies -Wno-name-shadowing -Wunused-foralls
-fprint-explicit-kinds -Werror -fprint-explicit-foralls -fprint-explicit-kinds -Wunused-do-bind
mixins: mixins:
base hiding (Prelude), base hiding (Prelude),
@ -39,6 +39,7 @@ common lang
DerivingStrategies DerivingStrategies
DerivingVia DerivingVia
DoAndIfThenElse DoAndIfThenElse
DuplicateRecordFields
EmptyCase EmptyCase
EmptyDataDecls EmptyDataDecls
EmptyDataDeriving EmptyDataDeriving
@ -88,10 +89,13 @@ common deps
build-depends: build-depends:
, aeson , aeson
, ansi-terminal , ansi-terminal
, base >=4.14 && <5 , base >=4.14 && <5
, base-compat , base-compat
, base16
, bytestring , bytestring
, cardano-binary
, cardano-prelude , cardano-prelude
, composition-prelude
, containers , containers
, data-default , data-default
, data-default-class , data-default-class
@ -100,6 +104,7 @@ common deps
, plutarch , plutarch
, plutarch-numeric , plutarch-numeric
, plutarch-safe-money , plutarch-safe-money
, plutarch-script-export
, plutus-core , plutus-core
, plutus-ledger-api , plutus-ledger-api
, plutus-tx , plutus-tx
@ -137,28 +142,27 @@ common exe-opts
library library
import: lang, deps import: lang, deps
exposed-modules: exposed-modules:
Agora.Aeson.Orphans
Agora.AuthorityToken Agora.AuthorityToken
Agora.Bootstrap
Agora.Effect Agora.Effect
Agora.Effect.GovernorMutation Agora.Effect.GovernorMutation
Agora.Effect.NoOp Agora.Effect.NoOp
Agora.Effect.TreasuryWithdrawal Agora.Effect.TreasuryWithdrawal
Agora.Governor Agora.Governor
Agora.Governor.Scripts Agora.Governor.Scripts
Agora.MultiSig Agora.Plutarch.Orphans
Agora.Proposal Agora.Proposal
Agora.Proposal.Scripts Agora.Proposal.Scripts
Agora.Proposal.Time Agora.Proposal.Time
Agora.SafeMoney Agora.SafeMoney
Agora.ScriptInfo Agora.Scripts
Agora.Stake Agora.Stake
Agora.Stake.Scripts Agora.Stake.Scripts
Agora.Treasury Agora.Treasury
Agora.Utils Agora.Utils
other-modules: other-modules:
Agora.Aeson.Orphans
Agora.Plutarch.Orphans
hs-source-dirs: agora hs-source-dirs: agora
library pprelude library pprelude
@ -182,15 +186,19 @@ library agora-specs
exposed-modules: exposed-modules:
Property.Generator Property.Generator
Property.Governor Property.Governor
Property.MultiSig
Sample.Effect.GovernorMutation Sample.Effect.GovernorMutation
Sample.Effect.TreasuryWithdrawal Sample.Effect.TreasuryWithdrawal
Sample.Governor Sample.Governor.Initialize
Sample.Proposal Sample.Governor.Mutate
Sample.Proposal.Advance
Sample.Proposal.Cosign
Sample.Proposal.Create
Sample.Proposal.Shared Sample.Proposal.Shared
Sample.Proposal.UnlockStake Sample.Proposal.UnlockStake
Sample.Proposal.Vote
Sample.Shared Sample.Shared
Sample.Stake Sample.Stake
Sample.Stake.SetDelegate
Sample.Treasury Sample.Treasury
Spec.AuthorityToken Spec.AuthorityToken
Spec.Effect.GovernorMutation Spec.Effect.GovernorMutation
@ -232,13 +240,12 @@ benchmark agora-bench
executable agora-scripts executable agora-scripts
import: lang, deps, exe-opts import: lang, deps, exe-opts
main-is: Scripts.hs main-is: Main.hs
hs-source-dirs: agora-scripts hs-source-dirs: agora-scripts
other-modules: Options other-modules:
build-depends: build-depends:
, agora , agora
, gitrev , gitrev
, optparse-applicative
executable agora-purescript-bridge executable agora-purescript-bridge
import: lang, deps, exe-opts import: lang, deps, exe-opts

View file

@ -20,6 +20,7 @@ import Data.Text.Encoding qualified as T
import PlutusLedgerApi.V1 qualified as Plutus import PlutusLedgerApi.V1 qualified as Plutus
import PlutusLedgerApi.V1.Bytes qualified as Plutus import PlutusLedgerApi.V1.Bytes qualified as Plutus
import PlutusLedgerApi.V1.Scripts qualified as Plutus
import PlutusLedgerApi.V1.Value qualified as Plutus import PlutusLedgerApi.V1.Value qualified as Plutus
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -109,6 +110,24 @@ deriving via
instance instance
(Aeson.FromJSON Plutus.ValidatorHash) (Aeson.FromJSON Plutus.ValidatorHash)
deriving via
(AsBase16Bytes Plutus.ScriptHash)
instance
(Aeson.ToJSON Plutus.ScriptHash)
deriving via
(AsBase16Bytes Plutus.ScriptHash)
instance
(Aeson.FromJSON Plutus.ScriptHash)
deriving via
(AsBase16Bytes Plutus.BuiltinByteString)
instance
(Aeson.ToJSON Plutus.BuiltinByteString)
deriving via
(AsBase16Bytes Plutus.BuiltinByteString)
instance
(Aeson.FromJSON Plutus.BuiltinByteString)
deriving via deriving via
(AsBase16Codec Plutus.Validator) (AsBase16Codec Plutus.Validator)
instance instance

View file

@ -12,7 +12,6 @@ module Agora.AuthorityToken (
AuthorityToken (..), AuthorityToken (..),
) where ) where
import GHC.Generics qualified as GHC
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
AmountGuarantees, AmountGuarantees,
KeyGuarantees, KeyGuarantees,
@ -33,7 +32,7 @@ import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (psymbolValueOf)
import "plutarch" Plutarch.Api.V1.Value (PValue (PValue)) import "plutarch" Plutarch.Api.V1.Value (PValue (PValue))
import Plutarch.Builtin (pforgetData) import Plutarch.Builtin (pforgetData)
import Plutarch.Extra.List (plookup) import Plutarch.Extra.List (plookup)
import Plutarch.Extra.TermCont (pguardC, pmatchC) import Plutarch.Extra.TermCont (pguardC, pletFieldsC, pmatchC)
import PlutusLedgerApi.V1.Value (AssetClass (AssetClass)) import PlutusLedgerApi.V1.Value (AssetClass (AssetClass))
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -53,7 +52,7 @@ newtype AuthorityToken = AuthorityToken
} }
deriving stock deriving stock
( -- | @since 0.1.0 ( -- | @since 0.1.0
GHC.Generic Generic
) )
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -73,7 +72,7 @@ authorityTokensValidIn :: Term s (PCurrencySymbol :--> PTxOut :--> PBool)
authorityTokensValidIn = phoistAcyclic $ authorityTokensValidIn = phoistAcyclic $
plam $ \authorityTokenSym txOut'' -> unTermCont $ do plam $ \authorityTokenSym txOut'' -> unTermCont $ do
PTxOut txOut' <- pmatchC txOut'' PTxOut txOut' <- pmatchC txOut''
txOut <- tcont $ pletFields @'["address", "value"] $ txOut' txOut <- pletFieldsC @'["address", "value"] $ txOut'
PAddress address <- pmatchC txOut.address PAddress address <- pmatchC txOut.address
PValue value' <- pmatchC txOut.value PValue value' <- pmatchC txOut.value
PMap value <- pmatchC value' PMap value <- pmatchC value'
@ -100,20 +99,18 @@ authorityTokensValidIn = phoistAcyclic $
{- | Assert that a single authority token has been burned. {- | Assert that a single authority token has been burned.
@since 0.1.0 @since 0.2.0
-} -}
singleAuthorityTokenBurned :: singleAuthorityTokenBurned ::
forall (keys :: KeyGuarantees) (amounts :: AmountGuarantees) (s :: S). forall (keys :: KeyGuarantees) (amounts :: AmountGuarantees) (s :: S).
Term s PCurrencySymbol -> Term s PCurrencySymbol ->
Term s (PAsData PTxInfo) -> Term s (PBuiltinList PTxInInfo) ->
Term s (PValue keys amounts) -> Term s (PValue keys amounts) ->
Term s PBool Term s PBool
singleAuthorityTokenBurned gatCs txInfo mint = unTermCont $ do singleAuthorityTokenBurned gatCs inputs mint = unTermCont $ do
let gatAmountMinted :: Term _ PInteger let gatAmountMinted :: Term _ PInteger
gatAmountMinted = psymbolValueOf # gatCs # mint gatAmountMinted = psymbolValueOf # gatCs # mint
txInfoF <- tcont $ pletFields @'["inputs"] $ txInfo
pure $ pure $
foldr1 foldr1
(#&&) (#&&)
@ -122,11 +119,11 @@ singleAuthorityTokenBurned gatCs txInfo mint = unTermCont $ do
pall pall
# plam # plam
( \txInInfo' -> unTermCont $ do ( \txInInfo' -> unTermCont $ do
PTxInInfo txInInfo <- pmatchC (pfromData txInInfo') PTxInInfo txInInfo <- pmatchC txInInfo'
let txOut' = pfield @"resolved" # txInInfo let txOut' = pfield @"resolved" # txInInfo
pure $ authorityTokensValidIn # gatCs # pfromData txOut' pure $ authorityTokensValidIn # gatCs # pfromData txOut'
) )
# txInfoF.inputs # inputs
] ]
{- | Policy given 'AuthorityToken' params. {- | Policy given 'AuthorityToken' params.
@ -137,9 +134,9 @@ authorityTokenPolicy :: AuthorityToken -> ClosedTerm PMintingPolicy
authorityTokenPolicy params = authorityTokenPolicy params =
plam $ \_redeemer ctx' -> plam $ \_redeemer ctx' ->
pmatch ctx' $ \(PScriptContext ctx') -> unTermCont $ do pmatch ctx' $ \(PScriptContext ctx') -> unTermCont $ do
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx' ctx <- pletFieldsC @'["txInfo", "purpose"] ctx'
PTxInfo txInfo' <- pmatchC $ pfromData ctx.txInfo PTxInfo txInfo' <- pmatchC $ pfromData ctx.txInfo
txInfo <- tcont $ pletFields @'["inputs", "mint", "outputs"] txInfo' txInfo <- pletFieldsC @'["inputs", "mint", "outputs"] txInfo'
let inputs = txInfo.inputs let inputs = txInfo.inputs
mintedValue = pfromData txInfo.mint mintedValue = pfromData txInfo.mint
AssetClass (govCs, govTn) = params.authority AssetClass (govCs, govTn) = params.authority
@ -158,9 +155,7 @@ authorityTokenPolicy params =
pguardC "All outputs only emit valid GATs" $ pguardC "All outputs only emit valid GATs" $
pall pall
# plam # plam
( (authorityTokensValidIn # ownSymbol #) (authorityTokensValidIn # ownSymbol #)
. pfromData
)
# txInfo.outputs # txInfo.outputs
pure $ popaque $ pconstant () pure $ popaque $ pconstant ()
) )

67
agora/Agora/Bootstrap.hs Normal file
View file

@ -0,0 +1,67 @@
{- | Module : Agora.Bootstrap
Maintainer : connor@mlabs.city
Description: Initialize a governance system
Initialize a governance system
-}
module Agora.Bootstrap (agoraScripts) where
import Agora.AuthorityToken (AuthorityToken (..), authorityTokenPolicy)
import Agora.Governor (Governor (..))
import Agora.Governor.Scripts (governorPolicy, governorValidator)
import Agora.Proposal.Scripts (proposalPolicy, proposalValidator)
import Agora.Scripts (AgoraScripts (AgoraScripts))
import Agora.Scripts qualified as Scripts
import Agora.Stake.Scripts (stakePolicy, stakeValidator)
import Agora.Treasury (treasuryValidator)
import Agora.Utils (
CompiledMintingPolicy (..),
CompiledValidator (..),
)
import Plutarch (Config)
import Plutarch.Api.V1 (
mintingPolicySymbol,
mkMintingPolicy,
mkValidator,
)
import PlutusLedgerApi.V1.Value (AssetClass (..))
{- | Parameterize and precompiled core scripts, given the
'Agora.Governor.Governor' parameters and plutarch configurations.
@since 0.2.0
-}
agoraScripts :: Config -> Governor -> AgoraScripts
agoraScripts conf gov = scripts
where
mkMintingPolicy' = mkMintingPolicy conf
mkValidator' = mkValidator conf
compiledGovernorPolicy = mkMintingPolicy' $ governorPolicy gov.gstOutRef
compiledGovernorValidator = mkValidator' $ governorValidator scripts
governorSymbol = mintingPolicySymbol compiledGovernorPolicy
governorAssetClass = AssetClass (governorSymbol, "")
authority = AuthorityToken governorAssetClass
compiledAuthorityPolicy = mkMintingPolicy' $ authorityTokenPolicy authority
authorityTokenSymbol = mintingPolicySymbol compiledAuthorityPolicy
compiledProposalPolicy = mkMintingPolicy' $ proposalPolicy governorAssetClass
compiledProposalValidator = mkValidator' $ proposalValidator scripts gov.maximumCosigners
compiledStakePolicy = mkMintingPolicy' $ stakePolicy gov.gtClassRef
compiledStakeValidator = mkValidator' $ stakeValidator scripts gov.gtClassRef
compiledTreasuryValidator = mkValidator' $ treasuryValidator authorityTokenSymbol
scripts =
AgoraScripts
{ Scripts.compiledGovernorPolicy = CompiledMintingPolicy compiledGovernorPolicy
, Scripts.compiledGovernorValidator = CompiledValidator compiledGovernorValidator
, Scripts.compiledStakePolicy = CompiledMintingPolicy compiledStakePolicy
, Scripts.compiledStakeValidator = CompiledValidator compiledStakeValidator
, Scripts.compiledProposalPolicy = CompiledMintingPolicy compiledProposalPolicy
, Scripts.compiledProposalValidator = CompiledValidator compiledProposalValidator
, Scripts.compiledTreasuryValidator = CompiledValidator compiledTreasuryValidator
, Scripts.compiledAuthorityTokenPolicy = CompiledMintingPolicy compiledAuthorityPolicy
}

View file

@ -8,8 +8,15 @@ Helpers for constructing effects.
module Agora.Effect (makeEffect) where module Agora.Effect (makeEffect) where
import Agora.AuthorityToken (singleAuthorityTokenBurned) import Agora.AuthorityToken (singleAuthorityTokenBurned)
import Plutarch.Api.V1 (PCurrencySymbol, PScriptPurpose (PSpending), PTxInfo, PTxOutRef, PValidator, PValue) import Plutarch.Api.V1 (
import Plutarch.Extra.TermCont (pguardC, pletC, pmatchC, ptryFromC) PCurrencySymbol,
PScriptPurpose (PSpending),
PTxInfo,
PTxOutRef,
PValidator,
PValue,
)
import Plutarch.Extra.TermCont (pguardC, pletC, pletFieldsC, pmatchC, ptryFromC)
import Plutarch.TryFrom () import Plutarch.TryFrom ()
import PlutusLedgerApi.V1.Value (CurrencySymbol) import PlutusLedgerApi.V1.Value (CurrencySymbol)
@ -23,33 +30,32 @@ import PlutusLedgerApi.V1.Value (CurrencySymbol)
-} -}
makeEffect :: makeEffect ::
forall (datum :: PType). forall (datum :: PType).
(PIsData datum, PTryFrom PData (PAsData datum)) => (PTryFrom PData datum, PIsData datum) =>
CurrencySymbol -> CurrencySymbol ->
(forall (s :: S). Term s PCurrencySymbol -> Term s datum -> Term s PTxOutRef -> Term s (PAsData PTxInfo) -> Term s POpaque) -> (forall (s :: S). Term s PCurrencySymbol -> Term s datum -> Term s PTxOutRef -> Term s (PAsData PTxInfo) -> Term s POpaque) ->
ClosedTerm PValidator ClosedTerm PValidator
makeEffect gatCs' f = makeEffect gatCs' f =
plam $ \datum _redeemer ctx' -> unTermCont $ do plam $ \datum _redeemer ctx' -> unTermCont $ do
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx' ctx <- pletFieldsC @'["txInfo", "purpose"] ctx'
txInfo' <- pletC ctx.txInfo
-- convert input datum, PData, into desierable type -- convert input datum, PData, into desierable type
-- the way this conversion is performed should be defined -- the way this conversion is performed should be defined
-- by PTryFrom for each datum in effect script. -- by PTryFrom for each datum in effect script.
(pfromData -> datum', _) <- ptryFromC datum (datum', _) <- ptryFromC datum
-- ensure purpose is Spending. -- ensure purpose is Spending.
PSpending txOutRef <- pmatchC $ pfromData ctx.purpose PSpending txOutRef <- pmatchC $ pfromData ctx.purpose
txOutRef' <- pletC (pfield @"_0" # txOutRef) txOutRef' <- pletC (pfield @"_0" # txOutRef)
-- fetch minted values to ensure single GAT is burned -- fetch minted values to ensure single GAT is burned
txInfo <- tcont $ pletFields @'["mint"] txInfo' txInfo <- pletFieldsC @'["mint", "inputs"] ctx.txInfo
let mint :: Term _ (PValue _ _) let mint :: Term _ (PValue _ _)
mint = txInfo.mint mint = txInfo.mint
-- fetch script context -- fetch script context
gatCs <- pletC $ pconstant gatCs' gatCs <- pletC $ pconstant gatCs'
pguardC "A single authority token has been burned" $ singleAuthorityTokenBurned gatCs txInfo' mint pguardC "A single authority token has been burned" $ singleAuthorityTokenBurned gatCs txInfo.inputs mint
-- run effect function -- run effect function
pure $ f gatCs datum' txOutRef' txInfo' pure $ f gatCs datum' txOutRef' ctx.txInfo

View file

@ -20,36 +20,28 @@ module Agora.Effect.GovernorMutation (
import Agora.Effect (makeEffect) import Agora.Effect (makeEffect)
import Agora.Governor ( import Agora.Governor (
Governor,
GovernorDatum, GovernorDatum,
PGovernorDatum, PGovernorDatum,
governorDatumValid, pisGovernorDatumValid,
)
import Agora.Governor.Scripts (
authorityTokenSymbolFromGovernor,
governorSTAssetClassFromGovernor,
) )
import Agora.Plutarch.Orphans () import Agora.Plutarch.Orphans ()
import Agora.Utils ( import Agora.Scripts (AgoraScripts, authorityTokenSymbol, governorSTAssetClass)
isScriptAddress,
mustBePDJust,
mustBePJust,
)
import GHC.Generics qualified as GHC
import Generics.SOP (Generic, I (I))
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
PTxOutRef, PTxOutRef,
PValidator, PValidator,
PValue, PValue,
) )
import Plutarch.Api.V1.ScriptContext (ptryFindDatum) import Plutarch.Api.V1.ScriptContext (pisScriptAddress, ptryFindDatum)
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (pvalueOf) import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (pvalueOf)
import Plutarch.DataRepr ( import Plutarch.DataRepr (
DerivePConstantViaData (..), DerivePConstantViaData (..),
PDataFields, PDataFields,
PIsDataReprInstances (PIsDataReprInstances),
) )
import Plutarch.Extra.TermCont (pguardC) import Plutarch.Extra.Maybe (
passertPDJust,
passertPJust,
)
import Plutarch.Extra.TermCont (pguardC, pletFieldsC)
import Plutarch.Lift (PConstantDecl, PLifted, PUnsafeLiftDecl) import Plutarch.Lift (PConstantDecl, PLifted, PUnsafeLiftDecl)
import PlutusLedgerApi.V1 (TxOutRef) import PlutusLedgerApi.V1 (TxOutRef)
import PlutusLedgerApi.V1.Value (AssetClass (..)) import PlutusLedgerApi.V1.Value (AssetClass (..))
@ -67,8 +59,12 @@ data MutateGovernorDatum = MutateGovernorDatum
, newDatum :: GovernorDatum , newDatum :: GovernorDatum
-- ^ The new settings for the governor. -- ^ The new settings for the governor.
} }
deriving stock (Show, GHC.Generic) deriving stock
deriving anyclass (Generic) ( -- | @since 0.1.ç
Show
, -- | @since 0.1.ç
Generic
)
PlutusTx.makeIsDataIndexed ''MutateGovernorDatum [('MutateGovernorDatum, 0)] PlutusTx.makeIsDataIndexed ''MutateGovernorDatum [('MutateGovernorDatum, 0)]
@ -89,18 +85,10 @@ newtype PMutateGovernorDatum (s :: S)
) )
) )
deriving stock deriving stock
( -- | @since 0.1.0
GHC.Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
Generic Generic
) )
deriving anyclass deriving anyclass
( -- | @since 0.1.0
PIsDataRepr
)
deriving
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
@ -110,7 +98,9 @@ newtype PMutateGovernorDatum (s :: S)
, -- | @since 0.1.0 , -- | @since 0.1.0
PEq PEq
) )
via (PIsDataReprInstances PMutateGovernorDatum)
instance DerivePlutusType PMutateGovernorDatum where
type DPTStrat _ = PlutusTypeData
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PMutateGovernorDatum where type PLifted PMutateGovernorDatum = MutateGovernorDatum instance PUnsafeLiftDecl PMutateGovernorDatum where type PLifted PMutateGovernorDatum = MutateGovernorDatum
@ -119,7 +109,7 @@ instance PUnsafeLiftDecl PMutateGovernorDatum where type PLifted PMutateGovernor
deriving via (DerivePConstantViaData MutateGovernorDatum PMutateGovernorDatum) instance (PConstantDecl MutateGovernorDatum) deriving via (DerivePConstantViaData MutateGovernorDatum PMutateGovernorDatum) instance (PConstantDecl MutateGovernorDatum)
-- | @since 0.1.0 -- | @since 0.1.0
deriving via PAsData (PIsDataReprInstances PMutateGovernorDatum) instance PTryFrom PData (PAsData PMutateGovernorDatum) deriving anyclass instance PTryFrom PData PMutateGovernorDatum
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -148,11 +138,14 @@ deriving via PAsData (PIsDataReprInstances PMutateGovernorDatum) instance PTryFr
@since 0.1.0 @since 0.1.0
-} -}
mutateGovernorValidator :: Governor -> ClosedTerm PValidator mutateGovernorValidator ::
mutateGovernorValidator gov = makeEffect (authorityTokenSymbolFromGovernor gov) $ -- | Lazy precompiled scripts. This is beacuse we need the symbol of GST.
AgoraScripts ->
ClosedTerm PValidator
mutateGovernorValidator as = makeEffect (authorityTokenSymbol as) $
\_gatCs (datum :: Term _ PMutateGovernorDatum) _ txInfo -> unTermCont $ do \_gatCs (datum :: Term _ PMutateGovernorDatum) _ txInfo -> unTermCont $ do
datumF <- tcont $ pletFields @'["newDatum", "governorRef"] datum datumF <- pletFieldsC @'["newDatum", "governorRef"] datum
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "datums"] txInfo txInfoF <- pletFieldsC @'["mint", "inputs", "outputs", "datums"] txInfo
let mint :: Term _ (PBuiltinList _) let mint :: Term _ (PBuiltinList _)
mint = pto $ pto $ pto $ pfromData txInfoF.mint mint = pto $ pto $ pto $ pfromData txInfoF.mint
@ -167,7 +160,7 @@ mutateGovernorValidator gov = makeEffect (authorityTokenSymbolFromGovernor gov)
( plam $ \inInfo count -> ( plam $ \inInfo count ->
let address = pfield @"address" #$ pfield @"resolved" # inInfo let address = pfield @"address" #$ pfield @"resolved" # inInfo
in pif in pif
(isScriptAddress # address) (pisScriptAddress # address)
(count + 1) (count + 1)
count count
) )
@ -177,7 +170,7 @@ mutateGovernorValidator gov = makeEffect (authorityTokenSymbolFromGovernor gov)
-- Find the governor input by looking for GST. -- Find the governor input by looking for GST.
let inputWithGST = let inputWithGST =
mustBePJust # "Governor input not found" #$ pfind passertPJust # "Governor input not found" #$ pfind
# phoistAcyclic # phoistAcyclic
( plam $ \inInfo -> ( plam $ \inInfo ->
let value = pfield @"value" #$ pfield @"resolved" # inInfo let value = pfield @"value" #$ pfield @"resolved" # inInfo
@ -185,7 +178,7 @@ mutateGovernorValidator gov = makeEffect (authorityTokenSymbolFromGovernor gov)
) )
# pfromData txInfoF.inputs # pfromData txInfoF.inputs
govInInfo <- tcont $ pletFields @'["outRef", "resolved"] $ inputWithGST govInInfo <- pletFieldsC @'["outRef", "resolved"] $ inputWithGST
-- The effect can only modify the governor UTXO referenced in the datum. -- The effect can only modify the governor UTXO referenced in the datum.
pguardC "Can only modify the pinned governor" $ pguardC "Can only modify the pinned governor" $
@ -196,9 +189,9 @@ mutateGovernorValidator gov = makeEffect (authorityTokenSymbolFromGovernor gov)
plength # pfromData txInfoF.outputs #== 1 plength # pfromData txInfoF.outputs #== 1
let govAddress = pfield @"address" #$ govInInfo.resolved let govAddress = pfield @"address" #$ govInInfo.resolved
govOutput' = pfromData $ phead # pfromData txInfoF.outputs govOutput' = phead # pfromData txInfoF.outputs
govOutput <- tcont $ pletFields @'["address", "value", "datumHash"] govOutput' govOutput <- pletFieldsC @'["address", "value", "datumHash"] govOutput'
pguardC "No output to the governor" $ pguardC "No output to the governor" $
govOutput.address #== govAddress govOutput.address #== govAddress
@ -207,15 +200,14 @@ mutateGovernorValidator gov = makeEffect (authorityTokenSymbolFromGovernor gov)
gstValueOf # govOutput.value #== 1 gstValueOf # govOutput.value #== 1
let governorOutputDatumHash = let governorOutputDatumHash =
mustBePDJust # "Governor output doesn't have datum" # govOutput.datumHash passertPDJust # "Governor output doesn't have datum" # govOutput.datumHash
governorOutputDatum = governorOutputDatum =
pfromData @PGovernorDatum $ passertPJust @PGovernorDatum # "Governor output datum not found"
mustBePJust # "Governor output datum not found" #$ ptryFindDatum # governorOutputDatumHash # txInfoF.datums
#$ ptryFindDatum # governorOutputDatumHash # txInfoF.datums
-- Ensure the output governor datum is what we want. -- Ensure the output governor datum is what we want.
pguardC "Unexpected governor datum" $ datumF.newDatum #== governorOutputDatum pguardC "Unexpected governor datum" $ datumF.newDatum #== governorOutputDatum
pguardC "New governor datum should be valid" $ governorDatumValid # governorOutputDatum pguardC "New governor datum should be valid" $ pisGovernorDatumValid # governorOutputDatum
return $ popaque $ pconstant () return $ popaque $ pconstant ()
where where
@ -223,4 +215,4 @@ mutateGovernorValidator gov = makeEffect (authorityTokenSymbolFromGovernor gov)
gstValueOf :: Term s (PValue _ _ :--> PInteger) gstValueOf :: Term s (PValue _ _ :--> PInteger)
gstValueOf = phoistAcyclic $ plam $ \v -> pvalueOf # v # pconstant cs # pconstant tn gstValueOf = phoistAcyclic $ plam $ \v -> pvalueOf # v # pconstant cs # pconstant tn
where where
AssetClass (cs, tn) = governorSTAssetClassFromGovernor gov AssetClass (cs, tn) = governorSTAssetClass as

View file

@ -7,11 +7,9 @@ A dumb effect that only burns its GAT.
-} -}
module Agora.Effect.NoOp (noOpValidator, PNoOp) where module Agora.Effect.NoOp (noOpValidator, PNoOp) where
import Control.Applicative (Const)
import Agora.Effect (makeEffect) import Agora.Effect (makeEffect)
import Agora.Plutarch.Orphans ()
import Plutarch.Api.V1 (PValidator) import Plutarch.Api.V1 (PValidator)
import Plutarch.TryFrom (PTryFrom (..))
import PlutusLedgerApi.V1.Value (CurrencySymbol) import PlutusLedgerApi.V1.Value (CurrencySymbol)
{- | Dummy datum for NoOp effect. {- | Dummy datum for NoOp effect.
@ -19,22 +17,23 @@ import PlutusLedgerApi.V1.Value (CurrencySymbol)
@since 0.1.0 @since 0.1.0
-} -}
newtype PNoOp (s :: S) = PNoOp (Term s PUnit) newtype PNoOp (s :: S) = PNoOp (Term s PUnit)
deriving deriving stock
( -- | @since 0.2.0
Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
PIsData PIsData
) )
via (DerivePNewtype PNoOp PUnit)
-- | @since 0.1.0 -- | @since 0.2.0
instance PTryFrom PData (PAsData PNoOp) where instance DerivePlutusType PNoOp where
type PTryFromExcess PData (PAsData PNoOp) = Const () type DPTStrat _ = PlutusTypeNewtype
ptryFrom' _ cont =
-- JUSTIFICATION: -- | @since 0.2.0
-- We don't care anything about data. instance PTryFrom PData (PAsData PNoOp)
-- It should always be reduced to Unit.
cont (pdata $ pcon $ PNoOp (pconstant ()), ())
{- | Dummy effect which can only burn its GAT. {- | Dummy effect which can only burn its GAT.
@ -42,4 +41,4 @@ instance PTryFrom PData (PAsData PNoOp) where
-} -}
noOpValidator :: CurrencySymbol -> ClosedTerm PValidator noOpValidator :: CurrencySymbol -> ClosedTerm PValidator
noOpValidator curr = makeEffect curr $ noOpValidator curr = makeEffect curr $
\_ (_datum :: Term s PNoOp) _ _ -> popaque (pconstant ()) \_ (_datum :: Term s (PAsData PNoOp)) _ _ -> popaque (pconstant ())

View file

@ -15,9 +15,6 @@ module Agora.Effect.TreasuryWithdrawal (
import Agora.Effect (makeEffect) import Agora.Effect (makeEffect)
import Agora.Plutarch.Orphans () import Agora.Plutarch.Orphans ()
import Agora.Utils (isPubKey)
import GHC.Generics qualified as GHC
import Generics.SOP (Generic, I (I))
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
AmountGuarantees (Positive), AmountGuarantees (Positive),
KeyGuarantees (Sorted), KeyGuarantees (Sorted),
@ -27,14 +24,13 @@ import Plutarch.Api.V1 (
PValue, PValue,
ptuple, ptuple,
) )
import Plutarch.Api.V1.ScriptContext (pfindTxInByTxOutRef) import Plutarch.Api.V1.ScriptContext (pfindTxInByTxOutRef, pisPubKey)
import "plutarch" Plutarch.Api.V1.Value (pnormalize) import "plutarch" Plutarch.Api.V1.Value (pnormalize)
import Plutarch.DataRepr ( import Plutarch.DataRepr (
DerivePConstantViaData (..), DerivePConstantViaData (..),
PDataFields, PDataFields,
PIsDataReprInstances (..),
) )
import Plutarch.Extra.TermCont (pguardC, pletC, pmatchC) import Plutarch.Extra.TermCont (pguardC, pletC, pletFieldsC, pmatchC)
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..)) import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))
import PlutusLedgerApi.V1.Credential (Credential) import PlutusLedgerApi.V1.Credential (Credential)
import PlutusLedgerApi.V1.Value (CurrencySymbol, Value) import PlutusLedgerApi.V1.Value (CurrencySymbol, Value)
@ -58,10 +54,6 @@ data TreasuryWithdrawalDatum = TreasuryWithdrawalDatum
( -- | @since 0.1.0 ( -- | @since 0.1.0
Show Show
, -- | @since 0.1.0 , -- | @since 0.1.0
GHC.Generic
)
deriving anyclass
( -- | @since 0.1.0
Generic Generic
) )
@ -87,15 +79,9 @@ newtype PTreasuryWithdrawalDatum (s :: S)
) )
deriving stock deriving stock
( -- | @since 0.1.0 ( -- | @since 0.1.0
GHC.Generic Generic
) )
deriving anyclass deriving anyclass
( -- | @since 0.1.0
Generic
, -- | @since 0.1.0
PIsDataRepr
)
deriving
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
@ -103,7 +89,9 @@ newtype PTreasuryWithdrawalDatum (s :: S)
, -- | @since 0.1.0 , -- | @since 0.1.0
PDataFields PDataFields
) )
via PIsDataReprInstances PTreasuryWithdrawalDatum
instance DerivePlutusType PTreasuryWithdrawalDatum where
type DPTStrat _ = PlutusTypeData
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PTreasuryWithdrawalDatum where instance PUnsafeLiftDecl PTreasuryWithdrawalDatum where
@ -116,10 +104,7 @@ deriving via
(PConstantDecl TreasuryWithdrawalDatum) (PConstantDecl TreasuryWithdrawalDatum)
-- | @since 0.1.0 -- | @since 0.1.0
deriving via instance PTryFrom PData PTreasuryWithdrawalDatum
PAsData (PIsDataReprInstances PTreasuryWithdrawalDatum)
instance
PTryFrom PData (PAsData PTreasuryWithdrawalDatum)
{- | Withdraws given list of values to specific target addresses. {- | Withdraws given list of values to specific target addresses.
It can be evoked by burning GAT. The transaction should have correct It can be evoked by burning GAT. The transaction should have correct
@ -143,26 +128,26 @@ deriving via
treasuryWithdrawalValidator :: forall {s :: S}. CurrencySymbol -> Term s PValidator treasuryWithdrawalValidator :: forall {s :: S}. CurrencySymbol -> Term s PValidator
treasuryWithdrawalValidator currSymbol = makeEffect currSymbol $ treasuryWithdrawalValidator currSymbol = makeEffect currSymbol $
\_cs (datum' :: Term _ PTreasuryWithdrawalDatum) txOutRef' txInfo' -> unTermCont $ do \_cs (datum' :: Term _ PTreasuryWithdrawalDatum) txOutRef' txInfo' -> unTermCont $ do
datum <- tcont $ pletFields @'["receivers", "treasuries"] datum' datum <- pletFieldsC @'["receivers", "treasuries"] datum'
txInfo <- tcont $ pletFields @'["outputs", "inputs"] txInfo' txInfo <- pletFieldsC @'["outputs", "inputs"] txInfo'
PJust ((pfield @"resolved" #) -> txOut) <- pmatchC $ pfindTxInByTxOutRef # txOutRef' # pfromData txInfo.inputs PJust ((pfield @"resolved" #) -> txOut) <- pmatchC $ pfindTxInByTxOutRef # txOutRef' # pfromData txInfo.inputs
effInput <- tcont $ pletFields @'["address", "value"] $ txOut effInput <- pletFieldsC @'["address", "value"] $ txOut
outputValues <- outputValues <-
pletC $ pletC $
pmap pmap
# plam # plam
( \(pfromData -> txOut') -> unTermCont $ do ( \txOut' -> unTermCont $ do
txOut <- tcont $ pletFields @'["address", "value"] $ txOut' txOut <- pletFieldsC @'["address", "value"] $ txOut'
let cred = pfield @"credential" # pfromData txOut.address let cred = pfield @"credential" # pfromData txOut.address
pure . pdata $ ptuple # cred # txOut.value pure . pdata $ ptuple # cred # txOut.value
) )
# txInfo.outputs # pfromData txInfo.outputs
inputValues <- inputValues <-
pletC $ pletC $
pmap pmap
# plam # plam
( \((pfield @"resolved" #) . pfromData -> txOut') -> unTermCont $ do ( \((pfield @"resolved" #) -> txOut') -> unTermCont $ do
txOut <- tcont $ pletFields @'["address", "value"] $ txOut' txOut <- pletFieldsC @'["address", "value"] $ txOut'
let cred = pfield @"credential" # pfromData txOut.address let cred = pfield @"credential" # pfromData txOut.address
pure . pdata $ ptuple # cred # txOut.value pure . pdata $ ptuple # cred # txOut.value
) )
@ -190,7 +175,7 @@ treasuryWithdrawalValidator currSymbol = makeEffect currSymbol $
pnot #$ pany pnot #$ pany
# plam # plam
( \x -> ( \x ->
effInput.address #== pfield @"address" # pfromData x effInput.address #== pfield @"address" # x
) )
# pfromData txInfo.outputs # pfromData txInfo.outputs
inputsAreOnlyTreasuriesOrCollateral = inputsAreOnlyTreasuriesOrCollateral =
@ -199,7 +184,7 @@ treasuryWithdrawalValidator currSymbol = makeEffect currSymbol $
( \((pfield @"_0" #) . pfromData -> cred) -> ( \((pfield @"_0" #) . pfromData -> cred) ->
cred #== pfield @"credential" # effInput.address cred #== pfield @"credential" # effInput.address
#|| pelem # cred # datum.treasuries #|| pelem # cred # datum.treasuries
#|| isPubKey # pfromData cred #|| pisPubKey # pfromData cred
) )
# inputValues # inputValues

View file

@ -20,7 +20,7 @@ module Agora.Governor (
-- * Utilities -- * Utilities
pgetNextProposalId, pgetNextProposalId,
getNextProposalId, getNextProposalId,
governorDatumValid, pisGovernorDatumValid,
) where ) where
import Agora.Proposal ( import Agora.Proposal (
@ -28,26 +28,29 @@ import Agora.Proposal (
PProposalThresholds (..), PProposalThresholds (..),
ProposalId (ProposalId), ProposalId (ProposalId),
ProposalThresholds, ProposalThresholds,
pisProposalThresholdsValid,
) )
import Agora.Proposal.Time ( import Agora.Proposal.Time (
MaxTimeRangeWidth, MaxTimeRangeWidth,
PMaxTimeRangeWidth, PMaxTimeRangeWidth,
PProposalTimingConfig, PProposalTimingConfig,
ProposalTimingConfig, ProposalTimingConfig,
pisMaxTimeRangeWidthValid,
pisProposalTimingConfigValid,
) )
import Agora.SafeMoney (GTTag) import Agora.SafeMoney (GTTag)
import Data.Tagged (Tagged (..)) import Data.Tagged (Tagged (..))
import GHC.Generics qualified as GHC
import Generics.SOP (Generic, I (I))
import Plutarch.DataRepr ( import Plutarch.DataRepr (
DerivePConstantViaData (..), DerivePConstantViaData (..),
PDataFields, PDataFields,
PIsDataReprInstances (PIsDataReprInstances),
) )
import Plutarch.Extra.Comonad (pextract) import Plutarch.Extra.IsData (
import Plutarch.Extra.TermCont (pletC, pmatchC) DerivePConstantViaEnum (..),
EnumIsData (..),
PlutusTypeEnumData,
)
import Plutarch.Extra.TermCont (pletFieldsC)
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..)) import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))
import Plutarch.SafeMoney (PDiscrete (..))
import PlutusLedgerApi.V1 (TxOutRef) import PlutusLedgerApi.V1 (TxOutRef)
import PlutusLedgerApi.V1.Value (AssetClass (..)) import PlutusLedgerApi.V1.Value (AssetClass (..))
import PlutusTx qualified import PlutusTx qualified
@ -68,8 +71,16 @@ data GovernorDatum = GovernorDatum
-- Will get copied over upon the creation of proposals. -- Will get copied over upon the creation of proposals.
, createProposalTimeRangeMaxWidth :: MaxTimeRangeWidth , createProposalTimeRangeMaxWidth :: MaxTimeRangeWidth
-- ^ The maximum valid duration of a transaction that creats a proposal. -- ^ The maximum valid duration of a transaction that creats a proposal.
, maximumProposalsPerStake :: Integer
-- ^ The maximum number of unfinished proposals that a stake is allowed to be
-- associated to.
} }
deriving stock (Show, GHC.Generic) deriving stock
( -- | @since 0.1.0
Show
, -- | @since 0.1.0
Generic
)
-- | @since 0.1.0 -- | @since 0.1.0
PlutusTx.makeIsDataIndexed ''GovernorDatum [('GovernorDatum, 0)] PlutusTx.makeIsDataIndexed ''GovernorDatum [('GovernorDatum, 0)]
@ -92,15 +103,23 @@ data GovernorRedeemer
MintGATs MintGATs
| -- | Allows effects to mutate the parameters. | -- | Allows effects to mutate the parameters.
MutateGovernor MutateGovernor
deriving stock (Show, GHC.Generic) deriving stock
( -- | @since 0.1.0
-- | @since 0.1.0 Show
PlutusTx.makeIsDataIndexed , -- | @since 0.1.0
''GovernorRedeemer Generic
[ ('CreateProposal, 0) , -- | @since 0.2.0
, ('MintGATs, 1) Enum
, ('MutateGovernor, 2) , -- | @since 0.2.0
] Bounded
)
deriving
( -- | @since 0.1.0
PlutusTx.ToData
, -- | @since 0.1.0
PlutusTx.FromData
)
via (EnumIsData GovernorRedeemer)
{- | Parameters for creating Governor scripts. {- | Parameters for creating Governor scripts.
@ -115,7 +134,12 @@ data Governor = Governor
-- ^ Arbitrary limit for maximum amount of cosigners on a proposal. -- ^ Arbitrary limit for maximum amount of cosigners on a proposal.
-- See `Agora.Proposal.proposalDatumValid`. -- See `Agora.Proposal.proposalDatumValid`.
} }
deriving stock (GHC.Generic) deriving stock
( -- | @since 0.1.0
Generic
, -- | @since 0.2.0
Show
)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -132,22 +156,15 @@ newtype PGovernorDatum (s :: S) = PGovernorDatum
, "nextProposalId" ':= PProposalId , "nextProposalId" ':= PProposalId
, "proposalTimings" ':= PProposalTimingConfig , "proposalTimings" ':= PProposalTimingConfig
, "createProposalTimeRangeMaxWidth" ':= PMaxTimeRangeWidth , "createProposalTimeRangeMaxWidth" ':= PMaxTimeRangeWidth
, "maximumProposalsPerStake" ':= PInteger
] ]
) )
} }
deriving stock deriving stock
( -- | @since 0.1.0
GHC.Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
Generic Generic
) )
deriving anyclass deriving anyclass
( -- | @since 0.1.0
PIsDataRepr
)
deriving
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
@ -157,7 +174,10 @@ newtype PGovernorDatum (s :: S) = PGovernorDatum
, -- | @since 0.1.0 , -- | @since 0.1.0
PEq PEq
) )
via PIsDataReprInstances PGovernorDatum
-- | @since 0.2.0
instance DerivePlutusType PGovernorDatum where
type DPTStrat _ = PlutusTypeData
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PGovernorDatum where type PLifted PGovernorDatum = GovernorDatum instance PUnsafeLiftDecl PGovernorDatum where type PLifted PGovernorDatum = GovernorDatum
@ -166,44 +186,45 @@ instance PUnsafeLiftDecl PGovernorDatum where type PLifted PGovernorDatum = Gove
deriving via (DerivePConstantViaData GovernorDatum PGovernorDatum) instance (PConstantDecl GovernorDatum) deriving via (DerivePConstantViaData GovernorDatum PGovernorDatum) instance (PConstantDecl GovernorDatum)
-- | @since 0.1.0 -- | @since 0.1.0
deriving via PAsData (PIsDataReprInstances PGovernorDatum) instance PTryFrom PData (PAsData PGovernorDatum) deriving anyclass instance PTryFrom PData PGovernorDatum
{- | Plutarch-level version of 'GovernorRedeemer'. {- | Plutarch-level version of 'GovernorRedeemer'.
@since 0.1.0 @since 0.1.0
-} -}
data PGovernorRedeemer (s :: S) data PGovernorRedeemer (s :: S)
= PCreateProposal (Term s (PDataRecord '[])) = PCreateProposal
| PMintGATs (Term s (PDataRecord '[])) | PMintGATs
| PMutateGovernor (Term s (PDataRecord '[])) | PMutateGovernor
deriving stock deriving stock
( -- | @since 0.1.0
GHC.Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
Generic Generic
, -- | @since 0.2.0
Enum
, -- | @since 0.2.0
Bounded
) )
deriving anyclass deriving anyclass
( -- | @since 0.1.0
PIsDataRepr
)
deriving
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
PIsData PIsData
, -- | @since 0.2.0
PEq
) )
via PIsDataReprInstances PGovernorRedeemer
-- | @since 0.2.0
instance PTryFrom PData (PAsData PGovernorRedeemer)
-- | @since 0.2.0
instance DerivePlutusType PGovernorRedeemer where
type DPTStrat _ = PlutusTypeEnumData
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PGovernorRedeemer where type PLifted PGovernorRedeemer = GovernorRedeemer instance PUnsafeLiftDecl PGovernorRedeemer where type PLifted PGovernorRedeemer = GovernorRedeemer
-- | @since 0.1.0 -- | @since 0.1.0
deriving via (DerivePConstantViaData GovernorRedeemer PGovernorRedeemer) instance (PConstantDecl GovernorRedeemer) deriving via (DerivePConstantViaEnum GovernorRedeemer PGovernorRedeemer) instance (PConstantDecl GovernorRedeemer)
-- | @since 0.1.0
deriving via PAsData (PIsDataReprInstances PGovernorRedeemer) instance PTryFrom PData (PAsData PGovernorRedeemer)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -227,28 +248,24 @@ getNextProposalId (ProposalId pid) = ProposalId $ pid + 1
@since 0.1.0 @since 0.1.0
-} -}
governorDatumValid :: Term s (PGovernorDatum :--> PBool) pisGovernorDatumValid :: Term s (PGovernorDatum :--> PBool)
governorDatumValid = phoistAcyclic $ pisGovernorDatumValid = phoistAcyclic $
plam $ \datum -> unTermCont $ do plam $ \datum -> unTermCont $ do
thresholds <- datumF <-
tcont $ pletFieldsC
pletFields @'["execute", "create", "vote"] $ @'[ "proposalThresholds"
pfield @"proposalThresholds" # datum , "proposalTimings"
, "createProposalTimeRangeMaxWidth"
PDiscrete execute' <- pmatchC thresholds.execute ]
PDiscrete draft' <- pmatchC thresholds.create datum
PDiscrete vote' <- pmatchC thresholds.vote
execute <- pletC $ pextract # execute'
draft <- pletC $ pextract # draft'
vote <- pletC $ pextract # vote'
pure $ pure $
foldr1 foldr1
(#&&) (#&&)
[ ptraceIfFalse "Execute threshold is less than or equal to" $ 0 #<= execute [ ptraceIfFalse "thresholds valid" $
, ptraceIfFalse "Draft threshold is less than or equal to " $ 0 #<= draft pisProposalThresholdsValid # pfromData datumF.proposalThresholds
, ptraceIfFalse "Vote threshold is less than or equal to " $ 0 #<= vote , ptraceIfFalse "timings valid" $
, ptraceIfFalse "Draft threshold is less than vote threshold" $ draft #<= vote pisProposalTimingConfigValid # pfromData datumF.proposalTimings
, ptraceIfFalse "Execute threshold is less than vote threshold" $ vote #< execute , ptraceIfFalse "time range valid" $
pisMaxTimeRangeWidthValid # datumF.createProposalTimeRangeMaxWidth
] ]

View file

@ -12,79 +12,40 @@ module Agora.Governor.Scripts (
-- * Scripts -- * Scripts
governorPolicy, governorPolicy,
governorValidator, governorValidator,
-- * Bridges
governorSTSymbolFromGovernor,
governorSTAssetClassFromGovernor,
proposalSTAssetClassFromGovernor,
stakeSTSymbolFromGovernor,
stakeFromGovernor,
stakeValidatorHashFromGovernor,
proposalFromGovernor,
proposalValidatorHashFromGovernor,
proposalSTSymbolFromGovernor,
stakeSTAssetClassFromGovernor,
governorValidatorHash,
authorityTokenFromGovernor,
authorityTokenSymbolFromGovernor,
) where ) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Agora.AuthorityToken ( import Agora.AuthorityToken (
AuthorityToken (..),
authorityTokenPolicy,
authorityTokensValidIn, authorityTokensValidIn,
singleAuthorityTokenBurned, singleAuthorityTokenBurned,
) )
import Agora.Governor ( import Agora.Governor (
Governor (gstOutRef, gtClassRef, maximumCosigners), GovernorRedeemer (..),
PGovernorDatum (PGovernorDatum), PGovernorDatum (PGovernorDatum),
PGovernorRedeemer (PCreateProposal, PMintGATs, PMutateGovernor),
governorDatumValid,
pgetNextProposalId, pgetNextProposalId,
pisGovernorDatumValid,
) )
import Agora.Proposal ( import Agora.Proposal (
PProposalDatum (..), PProposalDatum (..),
PProposalId (..),
PProposalStatus (PFinished),
PResultTag,
Proposal (..),
ProposalStatus (Draft, Locked), ProposalStatus (Draft, Locked),
pemptyVotesFor, phasNeutralEffect,
pisEffectsVotesCompatible,
pisVotesEmpty,
pneutralOption, pneutralOption,
proposalDatumValid,
pwinner, pwinner,
) )
import Agora.Proposal.Scripts (
proposalPolicy,
proposalValidator,
)
import Agora.Proposal.Time (createProposalStartingTime) import Agora.Proposal.Time (createProposalStartingTime)
import Agora.SafeMoney (GTTag) import Agora.Scripts (AgoraScripts, authorityTokenSymbol, governorSTSymbol, proposalSTSymbol, proposalValidatoHash, stakeSTSymbol)
import Agora.Stake ( import Agora.Stake (
PProposalLock (..), PProposalLock (..),
PStakeDatum (..), PStakeDatum (..),
Stake (..), pnumCreatedProposals,
)
import Agora.Stake.Scripts (
stakePolicy,
stakeValidator,
) )
import Agora.Utils ( import Agora.Utils (
findOutputsToAddress,
hasOnlyOneTokenOfCurrencySymbol,
mustBePDJust,
mustBePJust,
mustFindDatum', mustFindDatum',
scriptHashFromAddress,
validatorHashToAddress, validatorHashToAddress,
validatorHashToTokenName,
) )
import Plutarch.Extra.Record
--------------------------------------------------------------------------------
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
PAddress, PAddress,
PCurrencySymbol, PCurrencySymbol,
@ -95,38 +56,31 @@ import Plutarch.Api.V1 (
PTxOut, PTxOut,
PValidator, PValidator,
PValidatorHash, PValidatorHash,
PValue,
mintingPolicySymbol,
mkMintingPolicy,
mkValidator,
validatorHash,
) )
import Plutarch.Api.V1.AssetClass ( import Plutarch.Api.V1.AssetClass (
passetClass, passetClass,
passetClassValueOf, passetClassValueOf,
) )
import Plutarch.Api.V1.ScriptContext (
pfindOutputsToAddress,
pfindTxInByTxOutRef,
pisUTXOSpent,
pscriptHashFromAddress,
ptryFindDatum,
pvalueSpent,
)
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (phasOnlyOneTokenOfCurrencySymbol, psymbolValueOf)
import Plutarch.Extra.Field (pletAllC)
import Plutarch.Extra.IsData (pmatchEnumFromData)
import Plutarch.Extra.List (pfirstJust)
import Plutarch.Extra.Map ( import Plutarch.Extra.Map (
pkeys,
plookup, plookup,
plookup', plookup',
) )
import Plutarch.SafeMoney (PDiscrete (..), pvalueDiscrete') import Plutarch.Extra.Maybe (passertPDJust, passertPJust, pfromJust, pisDJust)
import Plutarch.TryFrom () import Plutarch.Extra.Record (mkRecordConstr, (.&), (.=))
import Plutarch.Extra.TermCont (pguardC, pletC, pletFieldsC, pmatchC, ptryFromC)
-------------------------------------------------------------------------------- import PlutusLedgerApi.V1 (TxOutRef)
import Plutarch.Api.V1.ScriptContext (pfindTxInByTxOutRef, pisUTXOSpent, ptryFindDatum, ptxSignedBy, pvalueSpent)
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (psymbolValueOf)
import Plutarch.Extra.Maybe (pisDJust)
import Plutarch.Extra.TermCont
import PlutusLedgerApi.V1 (
CurrencySymbol (..),
MintingPolicy,
)
import PlutusLedgerApi.V1.Scripts (ValidatorHash (..))
import PlutusLedgerApi.V1.Value (
AssetClass (..),
)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -157,16 +111,16 @@ import PlutusLedgerApi.V1.Value (
@since 0.1.0 @since 0.1.0
-} -}
governorPolicy :: Governor -> ClosedTerm PMintingPolicy governorPolicy :: TxOutRef -> ClosedTerm PMintingPolicy
governorPolicy gov = governorPolicy initialSpend =
plam $ \_ ctx' -> unTermCont $ do plam $ \_ ctx' -> unTermCont $ do
let oref = pconstant gov.gstOutRef let oref = pconstant initialSpend
PMinting ((pfield @"_0" #) -> ownSymbol) <- pmatchC (pfromData $ pfield @"purpose" # ctx') PMinting ((pfield @"_0" #) -> ownSymbol) <- pmatchC (pfromData $ pfield @"purpose" # ctx')
let ownAssetClass = passetClass # ownSymbol # pconstant "" let ownAssetClass = passetClass # ownSymbol # pconstant ""
txInfo = pfromData $ pfield @"txInfo" # ctx' txInfo = pfromData $ pfield @"txInfo" # ctx'
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "datums", "validRange"] txInfo txInfoF <- pletFieldsC @'["mint", "inputs", "outputs", "datums", "validRange"] txInfo
pguardC "Referenced utxo should be spent" $ pguardC "Referenced utxo should be spent" $
pisUTXOSpent # oref # txInfoF.inputs pisUTXOSpent # oref # txInfoF.inputs
@ -177,19 +131,21 @@ governorPolicy gov =
govOutput <- govOutput <-
pletC $ pletC $
mustBePJust passertPJust
# "Governor output not found" # "Governor output not found"
#$ pfind #$ pfind
# plam # plam
( \((pfield @"value" #) . pfromData -> value) -> ( \((pfield @"value" #) -> value) ->
psymbolValueOf # ownSymbol # value #== 1 psymbolValueOf # ownSymbol # value #== 1
) )
# pfromData txInfoF.outputs # pfromData txInfoF.outputs
let datumHash = pfield @"datumHash" # pfromData govOutput let datumHash = pfield @"datumHash" # govOutput
datum = mustFindDatum' @PGovernorDatum # datumHash # txInfoF.datums datum = mustFindDatum' @PGovernorDatum # datumHash # txInfoF.datums
pure $ popaque $ governorDatumValid # datum pguardC "Governor output datum valid" $ pisGovernorDatumValid # datum
pure $ popaque $ pconstant ()
{- | Validator for Governors. {- | Validator for Governors.
@ -277,62 +233,56 @@ governorPolicy gov =
@since 0.1.0 @since 0.1.0
-} -}
governorValidator :: Governor -> ClosedTerm PValidator governorValidator ::
governorValidator gov = -- | Lazy precompiled scripts.
AgoraScripts ->
ClosedTerm PValidator
governorValidator as =
plam $ \datum' redeemer' ctx' -> unTermCont $ do plam $ \datum' redeemer' ctx' -> unTermCont $ do
(pfromData -> redeemer, _) <- tcont $ ptryFrom redeemer' ctxF <- pletAllC ctx'
ctxF <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
txInfo' <- pletC $ pfromData $ ctxF.txInfo txInfo' <- pletC $ pfromData $ ctxF.txInfo
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "datums", "signatories", "validRange"] txInfo' txInfoF <- pletFieldsC @'["mint", "inputs", "outputs", "datums", "signatories", "validRange"] txInfo'
PSpending (pfromData . (pfield @"_0" #) -> ownInputRef) <- pmatchC $ pfromData ctxF.purpose PSpending (pfromData . (pfield @"_0" #) -> ownInputRef) <- pmatchC $ pfromData ctxF.purpose
((pfield @"resolved" #) -> ownInput) <- ((pfield @"resolved" #) -> ownInput) <-
pletC $ pletC $
mustBePJust # "Own input not found" passertPJust # "Own input not found"
#$ pfindTxInByTxOutRef # ownInputRef # txInfoF.inputs #$ pfindTxInByTxOutRef # ownInputRef # txInfoF.inputs
ownInputF <- tcont $ pletFields @'["address", "value"] ownInput ownInputF <- pletFieldsC @'["address", "value"] ownInput
let ownAddress = pfromData $ ownInputF.address let ownAddress = pfromData $ ownInputF.address
(pfromData -> (oldGovernorDatum :: Term _ PGovernorDatum), _) <- tcont $ ptryFrom datum' (oldGovernorDatum :: Term _ PGovernorDatum, _) <- ptryFromC datum'
oldGovernorDatumF <- oldGovernorDatumF <- pletAllC oldGovernorDatum
tcont $
pletFields
@'[ "proposalThresholds"
, "nextProposalId"
, "proposalTimings"
, "createProposalTimeRangeMaxWidth"
]
oldGovernorDatum
-- Check that GST will be returned to the governor. -- Check that GST will be returned to the governor.
let ownInputGSTAmount = psymbolValueOf # pgstSymbol # ownInputF.value let ownInputGSTAmount = psymbolValueOf # pgstSymbol # ownInputF.value
pguardC "Own input should have exactly one state token" $ pguardC "Own input should have exactly one state token" $
ownInputGSTAmount #== 1 ownInputGSTAmount #== 1
ownOutputs <- pletC $ findOutputsToAddress # txInfoF.outputs # ownAddress ownOutputs <- pletC $ pfindOutputsToAddress # txInfoF.outputs # ownAddress
pguardC "Exactly one utxo should be sent to the governor" $ pguardC "Exactly one utxo should be sent to the governor" $
plength # ownOutputs #== 1 plength # ownOutputs #== 1
ownOutput <- tcont $ pletFields @'["value", "datumHash"] $ phead # ownOutputs ownOutput <- pletFieldsC @'["value", "datumHash"] $ phead # ownOutputs
let ownOuputGSTAmount = psymbolValueOf # pgstSymbol # ownOutput.value let ownOuputGSTAmount = psymbolValueOf # pgstSymbol # ownOutput.value
pguardC "State token should stay at governor's address" $ pguardC "State token should stay at governor's address" $
ownOuputGSTAmount #== 1 ownOuputGSTAmount #== 1
-- Check that own output have datum of type 'GovernorDatum'. -- Check that own output have datum of type 'GovernorDatum'.
let outputGovernorStateDatumHash = let outputGovernorStateDatumHash =
mustBePDJust # "Governor output doesn't have datum" # ownOutput.datumHash passertPDJust # "Governor output doesn't have datum" # ownOutput.datumHash
newGovernorDatum <- newGovernorDatum <-
pletC $ pletC $
pfromData $ passertPJust # "Ouput governor state datum not found"
mustBePJust # "Ouput governor state datum not found" #$ ptryFindDatum # outputGovernorStateDatumHash # txInfoF.datums
#$ ptryFindDatum # outputGovernorStateDatumHash # txInfoF.datums
pguardC "New datum is not valid" $ governorDatumValid # newGovernorDatum pguardC "New datum is valid" $ pisGovernorDatumValid # newGovernorDatum
pure $ pure $
pmatch redeemer $ \case pmatchEnumFromData redeemer' $ \case
PCreateProposal _ -> unTermCont $ do Just CreateProposal -> unTermCont $ do
-- Check that the transaction advances proposal id. -- Check that the transaction advances proposal id.
let expectedNextProposalId = pgetNextProposalId # oldGovernorDatumF.nextProposalId let expectedNextProposalId = pgetNextProposalId # oldGovernorDatumF.nextProposalId
@ -344,6 +294,8 @@ governorValidator gov =
.& #proposalTimings .= oldGovernorDatumF.proposalTimings .& #proposalTimings .= oldGovernorDatumF.proposalTimings
.& #createProposalTimeRangeMaxWidth .& #createProposalTimeRangeMaxWidth
.= oldGovernorDatumF.createProposalTimeRangeMaxWidth .= oldGovernorDatumF.createProposalTimeRangeMaxWidth
.& #maximumProposalsPerStake
.= oldGovernorDatumF.maximumProposalsPerStake
) )
pguardC "Unexpected governor state datum" $ pguardC "Unexpected governor state datum" $
newGovernorDatum #== expectedNewDatum newGovernorDatum #== expectedNewDatum
@ -351,41 +303,38 @@ governorValidator gov =
-- Check that exactly one proposal token is being minted. -- Check that exactly one proposal token is being minted.
pguardC "Exactly one proposal token must be minted" $ pguardC "Exactly one proposal token must be minted" $
hasOnlyOneTokenOfCurrencySymbol # ppstSymbol # txInfoF.mint phasOnlyOneTokenOfCurrencySymbol # ppstSymbol # txInfoF.mint
-- Check that a stake is spent to create the propsal, -- Check that a stake is spent to create the propsal,
-- and the value it contains meets the requirement. -- and the value it contains meets the requirement.
stakeInput <- stakeInputs <-
pletC $ pletC $
mustBePJust # "Stake input not found" #$ pfind pfilter
# phoistAcyclic # phoistAcyclic
( plam $ ( plam $
\((pfield @"resolved" #) -> txOut') -> unTermCont $ do \((pfield @"value" #) . (pfield @"resolved" #) -> value) ->
txOut <- tcont $ pletFields @'["address", "value"] txOut' psymbolValueOf # psstSymbol # value #== 1
pure $
txOut.address #== pdata pstakeValidatorAddress
#&& psymbolValueOf # psstSymbol # txOut.value #== 1
) )
# pfromData txInfoF.inputs # pfromData txInfoF.inputs
stakeInputF <- tcont $ pletFields @'["datumHash", "value"] $ pfield @"resolved" # stakeInput pguardC "Can process only one stake" $
plength # stakeInputs #== 1
stakeInput <- pletC $ phead # stakeInputs
stakeInputF <- pletFieldsC @'["datumHash", "value"] $ pfield @"resolved" # stakeInput
pguardC "Stake input doesn't have datum" $ pguardC "Stake input doesn't have datum" $
pisDJust # stakeInputF.datumHash pisDJust # stakeInputF.datumHash
let stakeInputDatum = mustFindDatum' @PStakeDatum # stakeInputF.datumHash # txInfoF.datums let stakeInputDatum = mustFindDatum' @(PAsData PStakeDatum) # stakeInputF.datumHash # txInfoF.datums
stakeInputDatumF <- stakeInputDatumF <- pletAllC $ pto $ pfromData stakeInputDatum
tcont $ pletFields @["stakedAmount", "owner", "lockedBy"] stakeInputDatum
pguardC "Required amount of stake GTs should be presented" $ pguardC "Proposals created by the stake must not exceed the number stored in the governor." $
stakeInputDatumF.stakedAmount #== (pgtValueOf # stakeInputF.value) pnumCreatedProposals # stakeInputDatumF.lockedBy
#< oldGovernorDatumF.maximumProposalsPerStake
-- TODO: Is this required?
pguardC "Tx should be signed by the stake owner" $
ptxSignedBy # txInfoF.signatories # stakeInputDatumF.owner
-- Check that the newly minted PST is sent to the proposal validator, -- Check that the newly minted PST is sent to the proposal validator,
-- and the datum it carries is legal. -- and the datum it carries is legal.
@ -396,7 +345,7 @@ governorValidator gov =
# phoistAcyclic # phoistAcyclic
( plam $ ( plam $
\txOut' -> unTermCont $ do \txOut' -> unTermCont $ do
txOut <- tcont $ pletFields @'["address", "value"] txOut' txOut <- pletFieldsC @'["address", "value"] txOut'
pure $ pure $
txOut.address #== pdata pproposalValidatorAddress txOut.address #== pdata pproposalValidatorAddress
@ -411,114 +360,89 @@ governorValidator gov =
proposalOutputDatum' <- proposalOutputDatum' <-
pletC $ pletC $
mustFindDatum' @PProposalDatum mustFindDatum' @(PAsData PProposalDatum)
# outputDatumHash # outputDatumHash
# txInfoF.datums # txInfoF.datums
pguardC "Proposal datum must be valid" $ proposalOutputDatum <- pletAllC $ pto $ pfromData proposalOutputDatum'
proposalDatumValid' # proposalOutputDatum'
proposalOutputDatum <- let expectedStartingTime =
tcont $ pfromJust #$ createProposalStartingTime
pletFields
@'["effects", "cosigners", "proposalId", "votes"]
proposalOutputDatum'
pguardC "Proposal should have only one cosigner" $
plength # pfromData proposalOutputDatum.cosigners #== 1
let -- Votes should be empty at this point
expectedVotes = pemptyVotesFor # pfromData proposalOutputDatum.effects
expectedStartingTime =
createProposalStartingTime
# oldGovernorDatumF.createProposalTimeRangeMaxWidth # oldGovernorDatumF.createProposalTimeRangeMaxWidth
# txInfoF.validRange # txInfoF.validRange
-- Id, thresholds and timings should be copied from the old governor state datum.
expectedProposalOut =
mkRecordConstr
PProposalDatum
( #proposalId .= oldGovernorDatumF.nextProposalId
.& #effects .= proposalOutputDatum.effects
.& #status .= pconstantData Draft
.& #cosigners .= proposalOutputDatum.cosigners
.& #thresholds .= oldGovernorDatumF.proposalThresholds
.& #votes .= pdata expectedVotes
.& #timingConfig .= oldGovernorDatumF.proposalTimings
.& #startingTime .= pdata expectedStartingTime
)
pguardC "Datum correct" $ expectedProposalOut #== proposalOutputDatum' expectedCosigners = psingleton @PBuiltinList # stakeInputDatumF.owner
let cosigner = phead # pfromData proposalOutputDatum.cosigners pguardC "Proposal datum correct" $
foldl1
pguardC "Cosigner should be the stake owner" $ (#&&)
pdata stakeInputDatumF.owner #== cosigner [ ptraceIfFalse "has neutral effect" $
phasNeutralEffect # proposalOutputDatum.effects
, ptraceIfFalse "votes have valid shape" $
pisEffectsVotesCompatible # proposalOutputDatum.effects # proposalOutputDatum.votes
, ptraceIfFalse "votes are empty" $
pisVotesEmpty # proposalOutputDatum.votes
, ptraceIfFalse "id correct" $
proposalOutputDatum.proposalId #== oldGovernorDatumF.nextProposalId
, ptraceIfFalse "status is Draft" $
proposalOutputDatum.status #== pconstantData Draft
, ptraceIfFalse "cosigners correct" $
plistEquals # pfromData proposalOutputDatum.cosigners # expectedCosigners
, ptraceIfFalse "starting time correct" $
proposalOutputDatum.startingTime #== expectedStartingTime
, ptraceIfFalse "copy over configurations" $
proposalOutputDatum.thresholds #== oldGovernorDatumF.proposalThresholds
#&& proposalOutputDatum.timingConfig #== oldGovernorDatumF.proposalTimings
]
-- Check the output stake has been proposly updated. -- Check the output stake has been proposly updated.
let stakeOutputDatumHash =
passertPJust # "Output stake should be presented"
#$ pfirstJust
# phoistAcyclic
( plam
( \txOut -> unTermCont $ do
txOutF <- pletFieldsC @'["datumHash", "value"] txOut
stakeOutput <- pure $
pletC $ pif
mustBePJust (psymbolValueOf # psstSymbol # txOutF.value #== 1)
# "Stake output not found" ( pcon $
#$ pfind PJust $
# phoistAcyclic passertPDJust # "Output stake datum should be presented"
( plam $ # txOutF.datumHash
\txOut' -> unTermCont $ do )
txOut <- tcont $ pletFields @'["address", "value"] txOut' (pcon PNothing)
)
pure $ )
txOut.address #== pdata pstakeValidatorAddress # pfromData txInfoF.outputs
#&& psymbolValueOf # psstSymbol # txOut.value #== 1
)
# pfromData txInfoF.outputs
stakeOutputF <- tcont $ pletFields @'["datumHash", "value"] $ stakeOutput
pguardC "Staked GTs should be sent back to stake validator" $
stakeInputDatumF.stakedAmount #== (pgtValueOf # stakeOutputF.value)
let stakeOutputDatumHash = mustBePDJust # "Stake output should have datum" # stakeOutputF.datumHash
stakeOutputDatum = stakeOutputDatum =
mustBePJust # "Stake output not found" #$ ptryFindDatum # stakeOutputDatumHash # txInfoF.datums passertPJust @(PAsData PStakeDatum) # "Stake output datum presented"
#$ ptryFindDatum # stakeOutputDatumHash # txInfoF.datums
-- The stake should be locked by the newly created proposal. stakeOutputLocks =
pfromData $ pfield @"lockedBy" #$ pto $ pfromData stakeOutputDatum
let possibleVoteResults = pkeys #$ pto $ pfromData proposalOutputDatum.votes -- The stake should be locked by the newly created proposal.
newLock =
mkProposalLock :: Term _ (PProposalId :--> PAsData PResultTag :--> PAsData PProposalLock) mkRecordConstr
mkProposalLock = PCreated
phoistAcyclic $ ( #created .= oldGovernorDatumF.nextProposalId
plam )
( \pid rt' ->
pdata $
mkRecordConstr
PProposalLock
( #vote .= rt' .& #proposalTag .= pdata pid
)
)
-- Append new locks to existing locks -- Append new locks to existing locks
expectedProposalLocks = expectedProposalLocks =
pconcat # stakeInputDatumF.lockedBy pcons # pdata newLock # stakeInputDatumF.lockedBy
#$ pmap # (mkProposalLock # proposalOutputDatum.proposalId) # possibleVoteResults
expectedStakeOutputDatum = pguardC "Stake output locks correct" $
pdata $ plistEquals # stakeOutputLocks # expectedProposalLocks
mkRecordConstr
PStakeDatum
( #stakedAmount .= stakeInputDatumF.stakedAmount
.& #owner .= stakeInputDatumF.owner
.& #lockedBy .= pdata expectedProposalLocks
)
pguardC "Unexpected stake output datum" $ expectedStakeOutputDatum #== stakeOutputDatum
pure $ popaque $ pconstant () pure $ popaque $ pconstant ()
-------------------------------------------------------------------------- --------------------------------------------------------------------------
PMintGATs _ -> unTermCont $ do Just MintGATs -> unTermCont $ do
pguardC "Governor state should not be changed" $ newGovernorDatum #== oldGovernorDatum pguardC "Governor state should not be changed" $ newGovernorDatum #== oldGovernorDatum
-- Filter out proposal inputs and ouputs using PST and the address of proposal validator. -- Filter out proposal inputs and ouputs using PST and the address of proposal validator.
@ -527,78 +451,36 @@ governorValidator gov =
(psymbolValueOf # ppstSymbol #$ pvalueSpent # txInfoF.inputs) #== 1 (psymbolValueOf # ppstSymbol #$ pvalueSpent # txInfoF.inputs) #== 1
proposalInputF <- proposalInputF <-
tcont $ pletFieldsC @'["datumHash"] $
pletFields @'["datumHash"] $ pfield @"resolved"
pfield @"resolved" #$ passertPJust
#$ pfromData # "Proposal input not found"
$ mustBePJust
# "Proposal input not found"
#$ pfind
# plam
( \((pfield @"resolved" #) -> txOut) -> unTermCont $ do
txOutF <- tcont $ pletFields @'["address", "value"] txOut
pure $
psymbolValueOf # ppstSymbol # txOutF.value #== 1
#&& txOutF.address #== pdata pproposalValidatorAddress
)
# pfromData txInfoF.inputs
proposalOutputF <-
tcont $
pletFields @'["datumHash"] $
mustBePJust # "Proposal output not found"
#$ pfind #$ pfind
# plam # plam
( \txOut -> unTermCont $ do ( \((pfield @"resolved" #) -> txOut) -> unTermCont $ do
txOutF <- tcont $ pletFields @'["address", "value"] txOut txOutF <- pletFieldsC @'["address", "value"] txOut
pure $
psymbolValueOf # ppstSymbol # txOutF.value #== 1 pure $
#&& txOutF.address #== pdata pproposalValidatorAddress psymbolValueOf # ppstSymbol # txOutF.value #== 1
) #&& txOutF.address #== pdata pproposalValidatorAddress
# pfromData txInfoF.outputs )
# pfromData txInfoF.inputs
proposalInputDatum <- proposalInputDatum <-
pletC $ pletC $
mustFindDatum' @PProposalDatum mustFindDatum' @(PAsData PProposalDatum)
# proposalInputF.datumHash # proposalInputF.datumHash
# txInfoF.datums # txInfoF.datums
proposalOutputDatum <-
pletC $
mustFindDatum' @PProposalDatum
# proposalOutputF.datumHash
# txInfoF.datums
pguardC "Proposal datum must be valid" $
proposalDatumValid' # proposalInputDatum
#&& proposalDatumValid' # proposalOutputDatum
proposalInputDatumF <- proposalInputDatumF <-
tcont $ pletFieldsC @'["effects", "status", "thresholds", "votes"] $
pletFields @'["proposalId", "effects", "status", "cosigners", "thresholds", "votes", "timingConfig", "startingTime"] pto $ pfromData proposalInputDatum
proposalInputDatum
-- Check that the proposal state is advanced so that a proposal cannot be executed twice. -- Check that the proposal state is advanced so that a proposal cannot be executed twice.
pguardC "Proposal must be in locked(executable) state in order to execute effects" $ pguardC "Proposal must be in locked(executable) state in order to execute effects" $
proposalInputDatumF.status #== pconstantData Locked proposalInputDatumF.status #== pconstantData Locked
let expectedOutputProposalDatum =
mkRecordConstr
PProposalDatum
( #proposalId .= proposalInputDatumF.proposalId
.& #effects .= proposalInputDatumF.effects
.& #status .= pdata (pcon $ PFinished pdnil)
.& #cosigners .= proposalInputDatumF.cosigners
.& #thresholds .= proposalInputDatumF.thresholds
.& #votes .= proposalInputDatumF.votes
.& #timingConfig .= proposalInputDatumF.timingConfig
.& #startingTime .= proposalInputDatumF.startingTime
)
pguardC "Unexpected output proposal datum" $
pdata proposalOutputDatum #== pdata expectedOutputProposalDatum
-- TODO: anything else to check here? -- TODO: anything else to check here?
-- Find the highest votes and the corresponding tag. -- Find the highest votes and the corresponding tag.
@ -629,22 +511,22 @@ governorValidator gov =
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 :--> PAsData PTxOut :--> PBool) let gatOutputValidator' :: Term s (PMap _ PValidatorHash PDatumHash :--> PTxOut :--> PBool)
gatOutputValidator' = gatOutputValidator' =
phoistAcyclic $ phoistAcyclic $
plam plam
( \effects (pfromData -> output') -> unTermCont $ do ( \effects output' -> unTermCont $ do
output <- tcont $ pletFields @'["address", "datumHash"] $ output' output <- pletFieldsC @'["address", "datumHash"] output'
let scriptHash = let scriptHash =
mustBePJust # "GAT receiver is not a script" passertPJust # "GAT receiver is not a script"
#$ scriptHashFromAddress # output.address #$ pscriptHashFromAddress # output.address
datumHash = datumHash =
mustBePDJust # "Output to effect should have datum" passertPDJust # "Output to effect should have datum"
#$ output.datumHash #$ output.datumHash
expectedDatumHash = expectedDatumHash =
mustBePJust # "Receiver is not in the effect list" passertPJust # "Receiver is not in the effect list"
#$ plookup # scriptHash # effects #$ plookup # scriptHash # effects
pure $ pure $
@ -657,197 +539,51 @@ governorValidator gov =
gatOutputValidator = gatOutputValidator' # effectGroup gatOutputValidator = gatOutputValidator' # effectGroup
pure $ pguardC "GATs valid" $
popaque $ pfoldr
pfoldr # plam
# plam ( \txOut r ->
( \txOut r -> 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 #&& gatOutputValidator # txOut) $ pconstant False )
) # pconstant True
# pconstant True # pfromData txInfoF.outputs
# pfromData txInfoF.outputs
pure $ popaque $ pconstant ()
-------------------------------------------------------------------------- --------------------------------------------------------------------------
PMutateGovernor _ -> unTermCont $ do Just MutateGovernor -> unTermCont $ do
-- Check that a GAT is burnt. -- Check that a GAT is burnt.
pure $ popaque $ singleAuthorityTokenBurned patSymbol ctxF.txInfo txInfoF.mint pguardC "One valid GAT burnt" $
where singleAuthorityTokenBurned patSymbol txInfoF.inputs txInfoF.mint
-- Get th amount of governance tokens in a value.
pgtValueOf :: Term s (PValue _ _ :--> PDiscrete GTTag)
pgtValueOf = phoistAcyclic $ pvalueDiscrete' gov.gtClassRef
pure $ popaque $ pconstant ()
--------------------------------------------------------------------------
Nothing -> ptraceError "Unknown redeemer"
where
-- The currency symbol of authority token. -- The currency symbol of authority token.
patSymbol :: Term s PCurrencySymbol patSymbol :: Term s PCurrencySymbol
patSymbol = phoistAcyclic $ pconstant $ authorityTokenSymbolFromGovernor gov patSymbol = pconstant $ authorityTokenSymbol as
-- The currency symbol of the proposal state token. -- The currency symbol of the proposal state token.
ppstSymbol :: Term s PCurrencySymbol ppstSymbol :: Term s PCurrencySymbol
ppstSymbol = ppstSymbol = pconstant $ proposalSTSymbol as
let AssetClass (sym, _) = proposalSTAssetClassFromGovernor gov
in phoistAcyclic $ pconstant sym
-- Is a proposal state datum valid?
proposalDatumValid' :: Term s (PProposalDatum :--> PBool)
proposalDatumValid' =
let params = proposalFromGovernor gov
in phoistAcyclic $ proposalDatumValid params
-- The address of the proposal validator. -- The address of the proposal validator.
pproposalValidatorAddress :: Term s PAddress pproposalValidatorAddress :: Term s PAddress
pproposalValidatorAddress = pproposalValidatorAddress =
let vh = proposalValidatorHashFromGovernor gov pconstant $
in phoistAcyclic $ pconstant $ validatorHashToAddress vh validatorHashToAddress $
proposalValidatoHash as
-- The address of the stake validator.
pstakeValidatorAddress :: Term s PAddress
pstakeValidatorAddress =
let vh = stakeValidatorHashFromGovernor gov
in phoistAcyclic $ pconstant $ validatorHashToAddress vh
-- The currency symbol of the stake state token. -- The currency symbol of the stake state token.
psstSymbol :: Term s PCurrencySymbol psstSymbol :: Term s PCurrencySymbol
psstSymbol = psstSymbol = pconstant $ stakeSTSymbol as
let sym = stakeSTSymbolFromGovernor gov
in phoistAcyclic $ pconstant sym
-- The currency symbol of the governor state token. -- The currency symbol of the governor state token.
pgstSymbol :: Term s PCurrencySymbol pgstSymbol :: Term s PCurrencySymbol
pgstSymbol = pgstSymbol = pconstant $ governorSTSymbol as
let sym = governorSTSymbolFromGovernor gov
in phoistAcyclic $ pconstant sym
--------------------------------------------------------------------------------
{- | Get the 'CurrencySymbol' of GST.
@since 0.1.0
-}
governorSTSymbolFromGovernor :: Governor -> CurrencySymbol
governorSTSymbolFromGovernor gov = mintingPolicySymbol policy
where
policy :: MintingPolicy
policy = mkMintingPolicy $ governorPolicy gov
{- | Get the 'AssetClass' of GST.
@since 0.1.0
-}
governorSTAssetClassFromGovernor :: Governor -> AssetClass
governorSTAssetClassFromGovernor gov = AssetClass (symbol, "")
where
symbol :: CurrencySymbol
symbol = governorSTSymbolFromGovernor gov
{- | Get the 'CurrencySymbol' of the proposal state token.
@since 0.1.0
-}
proposalSTSymbolFromGovernor :: Governor -> CurrencySymbol
proposalSTSymbolFromGovernor gov = symbol
where
gstAC = governorSTAssetClassFromGovernor gov
policy = mkMintingPolicy $ proposalPolicy gstAC
symbol = mintingPolicySymbol policy
{- | Get the 'AssetClass' of the proposal state token.
@since 0.1.0
-}
proposalSTAssetClassFromGovernor :: Governor -> AssetClass
proposalSTAssetClassFromGovernor gov = AssetClass (symbol, "")
where
symbol = proposalSTSymbolFromGovernor gov
{- | Get the 'CurrencySymbol' of the stake token/
@since 0.1.0
-}
stakeSTSymbolFromGovernor :: Governor -> CurrencySymbol
stakeSTSymbolFromGovernor gov = mintingPolicySymbol policy
where
policy = mkMintingPolicy $ stakePolicy gov.gtClassRef
{- | Get the 'AssetClass' of the stake token.
Note that the token is tagged with the hash of the stake validator.
See 'Agora.Stake.Script.stakePolicy'.
@since 0.1.0
-}
stakeSTAssetClassFromGovernor :: Governor -> AssetClass
stakeSTAssetClassFromGovernor gov = AssetClass (symbol, tokenName)
where
symbol = stakeSTSymbolFromGovernor gov
-- Tag with the address where the token is being sent to.
tokenName = validatorHashToTokenName $ stakeValidatorHashFromGovernor gov
{- | Get the 'Stake' parameter, given the 'Governor' parameter.
@since 0.1.0
-}
stakeFromGovernor :: Governor -> Stake
stakeFromGovernor gov =
Stake gov.gtClassRef $
proposalSTAssetClassFromGovernor gov
{- | Get the hash of 'Agora.Stake.Script.stakePolicy'.
@since 0.1.0
-}
stakeValidatorHashFromGovernor :: Governor -> ValidatorHash
stakeValidatorHashFromGovernor gov = validatorHash validator
where
params = stakeFromGovernor gov
validator = mkValidator $ stakeValidator params
{- | Get the 'Proposal' parameter, given the 'Governor' parameter.
@since 0.1.0
-}
proposalFromGovernor :: Governor -> Proposal
proposalFromGovernor gov = Proposal gstAC sstAC mc
where
gstAC = governorSTAssetClassFromGovernor gov
mc = gov.maximumCosigners
sstAC = stakeSTAssetClassFromGovernor gov
{- | Get the hash of 'Agora.Proposal.proposalPolicy'.
@since 0.1.0
-}
proposalValidatorHashFromGovernor :: Governor -> ValidatorHash
proposalValidatorHashFromGovernor gov = validatorHash validator
where
params = proposalFromGovernor gov
validator = mkValidator $ proposalValidator params
{- | Get the hash of 'Agora.Proposal.proposalValidator'.
@since 0.1.0
-}
governorValidatorHash :: Governor -> ValidatorHash
governorValidatorHash gov = validatorHash validator
where
validator = mkValidator $ governorValidator gov
{- | Get the 'AuthorityToken' parameter given the 'Governor' parameter.
@since 0.1.0
-}
authorityTokenFromGovernor :: Governor -> AuthorityToken
authorityTokenFromGovernor gov = AuthorityToken $ governorSTAssetClassFromGovernor gov
{- | Get the 'CurrencySymbol' of the authority token.
@since 0.1.0
-}
authorityTokenSymbolFromGovernor :: Governor -> CurrencySymbol
authorityTokenSymbolFromGovernor gov = mintingPolicySymbol policy
where
policy = mkMintingPolicy $ authorityTokenPolicy params
params = authorityTokenFromGovernor gov

View file

@ -1,136 +0,0 @@
{-# LANGUAGE TemplateHaskell #-}
{- |
Module : Agora.MultiSig
Maintainer : riley_kilgore@outlook.com
Description: A basic N of M multisignature validation function.
A basic N of M multisignature validation function.
-}
module Agora.MultiSig (
validatedByMultisig,
pvalidatedByMultisig,
PMultiSig (..),
MultiSig (..),
) where
import GHC.Generics qualified as GHC
import Generics.SOP (Generic, I (I))
import Plutarch.Api.V1 (
PPubKeyHash,
PTxInfo (..),
)
import Plutarch.DataRepr (
DerivePConstantViaData (DerivePConstantViaData),
PDataFields,
PIsDataReprInstances (PIsDataReprInstances),
)
import Plutarch.Lift (
PConstantDecl,
PLifted,
PUnsafeLiftDecl,
)
import PlutusLedgerApi.V1.Crypto (PubKeyHash)
import PlutusTx qualified
import Prelude
--------------------------------------------------------------------------------
{- | A MultiSig represents a proof that a particular set of signatures
are present on a transaction.
@since 0.1.0
-}
data MultiSig = MultiSig
{ keys :: [PubKeyHash]
-- ^ List of PubKeyHashes that must be present in the list of signatories.
, minSigs :: Integer
}
deriving stock
( -- | @since 0.1.0
GHC.Generic
, -- | @since 0.1.0
Eq
, -- | @since 0.1.0
Show
)
deriving anyclass
( -- | @since 0.1.0
Generic
)
PlutusTx.makeLift ''MultiSig
PlutusTx.unstableMakeIsData ''MultiSig
{- | Plutarch-level MultiSig
@since 0.1.0
-}
newtype PMultiSig (s :: S) = PMultiSig
{ getMultiSig ::
Term
s
( PDataRecord
'[ "keys" ':= PBuiltinList (PAsData PPubKeyHash)
, "minSigs" ':= PInteger
]
)
}
deriving stock
( -- | @since 0.1.0
GHC.Generic
)
deriving anyclass
( -- | @since 0.1.0
Generic
)
deriving anyclass
( -- | @since 0.1.0
PIsDataRepr
)
deriving
( -- | @since 0.1.0
PlutusType
, -- | @since 0.1.0
PIsData
, -- | @since 0.1.0
PDataFields
)
via (PIsDataReprInstances PMultiSig)
-- | @since 0.1.0
instance PUnsafeLiftDecl PMultiSig where type PLifted PMultiSig = MultiSig
-- | @since 0.1.0
deriving via (DerivePConstantViaData MultiSig PMultiSig) instance (PConstantDecl MultiSig)
--------------------------------------------------------------------------------
{- | Check if a Haskell-level MultiSig signs this transaction.
@since 0.1.0
-}
validatedByMultisig :: MultiSig -> Term s (PTxInfo :--> PBool)
validatedByMultisig params =
phoistAcyclic $
pvalidatedByMultisig # pconstant params
{- | Check if a Plutarch-level MultiSig signs this transaction.
@since 0.1.0
-}
pvalidatedByMultisig :: Term s (PMultiSig :--> PTxInfo :--> PBool)
pvalidatedByMultisig =
phoistAcyclic $
plam $ \multi' txInfo -> unTermCont $ do
multi <- tcont $ pletFields @'["keys", "minSigs"] multi'
let signatories = pfield @"signatories" # txInfo
pure $
pfromData multi.minSigs
#<= ( plength #$ pfilter
# plam
( \a ->
pelem # a # pfromData signatories
)
# multi.keys
)

View file

@ -3,132 +3,37 @@
{- FIXME: All of the following instances and {- FIXME: All of the following instances and
types ought to belong in either plutarch or types ought to belong in either plutarch or
plutarch-extra. plutarch-extra.
A number of these have been "stolen" from Mango's
PR: https://github.com/Plutonomicon/plutarch/pull/438/
-} -}
module Agora.Plutarch.Orphans () where module Agora.Plutarch.Orphans () where
import Control.Arrow (first) import Plutarch.Api.V1 (PDatumHash (..))
import Plutarch.Api.V1 (PAddress, PCredential, PCurrencySymbol, PDatumHash, PMap, PMaybeData, PPOSIXTime, PPubKeyHash, PStakingCredential, PTokenName, PTxId, PTxOutRef, PValidatorHash, PValue) import Plutarch.Builtin (PIsData (..))
import Plutarch.Builtin (PBuiltinMap) import Plutarch.Extra.TermCont (ptryFromC)
import Plutarch.DataRepr (PIsDataReprInstances (..)) import Plutarch.TryFrom (PTryFrom (..))
import Plutarch.Numeric.Additive (AdditiveSemigroup ((+)))
import Plutarch.Reducible (Reduce, Reducible)
import Plutarch.TryFrom (PTryFrom (PTryFromExcess, ptryFrom'))
import Plutarch.Unsafe (punsafeCoerce) import Plutarch.Unsafe (punsafeCoerce)
import Prelude hiding ((+))
instance Reducible (f x y) => Reducible (Flip f y x) where newtype Flip f a b = Flip (f b a) deriving stock (Generic)
type Reduce (Flip f y x) = Reduce (f x y)
newtype Flip f a b = Flip (f b a)
-- | @since 0.1.0
instance PTryFrom PData (PAsData b) => PTryFrom PData (PAsData (DerivePNewtype c b)) where
type
PTryFromExcess PData (PAsData (DerivePNewtype c b)) =
PTryFromExcess PData (PAsData b)
ptryFrom' d k =
ptryFrom' @_ @(PAsData b) d $ k . first punsafeCoerce
-- | @since 0.1.0
instance PTryFrom PData (PAsData PPubKeyHash) where
type PTryFromExcess PData (PAsData PPubKeyHash) = Flip Term PPubKeyHash
ptryFrom' opq = runTermCont $ do
(wrapped :: Term _ (PAsData PByteString), unwrapped :: Term _ PByteString) <-
tcont $ ptryFrom @(PAsData PByteString) opq
tcont $ \f -> pif (plengthBS # unwrapped #== 28) (f ()) (ptraceError "a PubKeyHash should be 28 bytes long")
pure (punsafeCoerce wrapped, punsafeCoerce unwrapped)
-- | @since 0.1.0
instance AdditiveSemigroup (Term s PPOSIXTime) where
(punsafeCoerce @_ @_ @PInteger -> x) + (punsafeCoerce @_ @_ @PInteger -> y) = punsafeCoerce $ x + y
-- | @since 0.1.0
deriving via
PAsData (DerivePNewtype PPOSIXTime PInteger)
instance
PTryFrom PData (PAsData PPOSIXTime)
-- | @since 0.1.0
deriving via
PAsData (PIsDataReprInstances PTxId)
instance
PTryFrom PData (PAsData PTxId)
-- | @since 0.1.0
deriving via
PAsData (PIsDataReprInstances PTxOutRef)
instance
PTryFrom PData (PAsData PTxOutRef)
-- | @since 0.1.0
deriving via
PAsData (DerivePNewtype (PMap g k v) (PBuiltinMap k v))
instance
( PTryFrom PData (PAsData k)
, PTryFrom PData (PAsData v)
) =>
PTryFrom PData (PAsData (PMap g k v))
-- | @since 0.1.0
instance PTryFrom PData (PAsData PValidatorHash) where
type PTryFromExcess PData (PAsData PValidatorHash) = Flip Term PValidatorHash
ptryFrom' opq = runTermCont $ do
(wrapped :: Term _ (PAsData PByteString), unwrapped :: Term _ PByteString) <-
tcont $ ptryFrom @(PAsData PByteString) opq
tcont $ \f -> pif (plengthBS # unwrapped #== 28) (f ()) (ptraceError "a ValidatorHash should be 28 bytes long")
pure (punsafeCoerce wrapped, punsafeCoerce unwrapped)
-- | @since 0.1.0 -- | @since 0.1.0
instance PTryFrom PData (PAsData PDatumHash) where instance PTryFrom PData (PAsData PDatumHash) where
type PTryFromExcess PData (PAsData PDatumHash) = Flip Term PDatumHash type PTryFromExcess PData (PAsData PDatumHash) = Flip Term PDatumHash
ptryFrom' opq = runTermCont $ do ptryFrom' opq = runTermCont $ do
(wrapped :: Term _ (PAsData PByteString), unwrapped :: Term _ PByteString) <- (pfromData -> unwrapped, _) <- ptryFromC @(PAsData PByteString) opq
tcont $ ptryFrom @(PAsData PByteString) opq
tcont $ \f -> pif (plengthBS # unwrapped #== 32) (f ()) (ptraceError "a DatumHash should be 32 bytes long")
pure (punsafeCoerce wrapped, punsafeCoerce unwrapped)
-- | @since 0.1.0 tcont $ \f ->
deriving via pif
PAsData (DerivePNewtype PCurrencySymbol PByteString) -- Blake2b_256 hash: 256 bits/32 bytes.
instance (plengthBS # unwrapped #== 32)
PTryFrom PData (PAsData PCurrencySymbol) (f ())
(ptraceError "ptryFrom(PDatumHash): must be 32 bytes long")
-- | @since 0.1.0 pure (punsafeCoerce opq, pcon $ PDatumHash unwrapped)
deriving via
PAsData (DerivePNewtype PTokenName PByteString)
instance
PTryFrom PData (PAsData PTokenName)
-- | @since 0.1.0 -- | @since 0.2.0
deriving via instance PTryFrom PData (PAsData PUnit)
PAsData (DerivePNewtype (PValue k v) (PMap k PCurrencySymbol (PMap k PTokenName PInteger)))
instance
PTryFrom PData (PAsData (PValue k v))
-- | @since 0.1.0 -- | @since 0.2.0
deriving via instance (PIsData a) => PIsData (PAsData a) where
PAsData (PIsDataReprInstances (PMaybeData a)) pfromDataImpl = punsafeCoerce
instance pdataImpl = pdataImpl . pfromData
PTryFrom PData (PAsData a) => PTryFrom PData (PAsData (PMaybeData a))
-- | @since 0.1.0
deriving via
PAsData (PIsDataReprInstances PAddress)
instance
PTryFrom PData (PAsData PAddress)
-- | @since 0.1.0
deriving via
PAsData (PIsDataReprInstances PCredential)
instance
PTryFrom PData (PAsData PCredential)
-- | @since 0.1.0
deriving via
PAsData (PIsDataReprInstances PStakingCredential)
instance
PTryFrom PData (PAsData PStakingCredential)

View file

@ -9,7 +9,8 @@ Proposal scripts encoding effects that operate on the system.
-} -}
module Agora.Proposal ( module Agora.Proposal (
-- * Haskell-land -- * Haskell-land
Proposal (..),
-- Proposal (..),
ProposalDatum (..), ProposalDatum (..),
ProposalRedeemer (..), ProposalRedeemer (..),
ProposalStatus (..), ProposalStatus (..),
@ -29,20 +30,21 @@ module Agora.Proposal (
PResultTag (..), PResultTag (..),
-- * Plutarch helpers -- * Plutarch helpers
proposalDatumValid, phasNeutralEffect,
pemptyVotesFor, pisEffectsVotesCompatible,
pisVotesEmpty,
pwinner, pwinner,
pwinner', pwinner',
pneutralOption, pneutralOption,
pretractVotes, pretractVotes,
pisProposalThresholdsValid,
) where ) where
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 Agora.Utils (mustBePJust)
import Data.Tagged (Tagged) import Data.Tagged (Tagged)
import GHC.Generics qualified as GHC import Generics.SOP qualified as SOP
import Generics.SOP (Generic, I (I))
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
KeyGuarantees (Unsorted), KeyGuarantees (Unsorted),
PDatumHash, PDatumHash,
@ -50,19 +52,31 @@ import Plutarch.Api.V1 (
PPubKeyHash, PPubKeyHash,
PValidatorHash, PValidatorHash,
) )
import Plutarch.DataRepr (DerivePConstantViaData (..), PDataFields, PIsDataReprInstances (..)) import Plutarch.Api.V1.AssocMap qualified as PAssocMap
import Plutarch.Extra.List (pnotNull) import Plutarch.DataRepr (DerivePConstantViaData (..), PDataFields)
import Plutarch.Extra.Comonad (pextract)
import Plutarch.Extra.Field (pletAllC)
import Plutarch.Extra.Function (pbuiltinUncurry)
import Plutarch.Extra.IsData (
DerivePConstantViaDataList (..),
DerivePConstantViaEnum (..),
EnumIsData (..),
PlutusTypeEnumData,
ProductIsData (ProductIsData),
)
import Plutarch.Extra.List (pfirstJust)
import Plutarch.Extra.Map qualified as PM import Plutarch.Extra.Map qualified as PM
import Plutarch.Extra.Map.Unsorted qualified as PUM import Plutarch.Extra.Map.Unsorted qualified as PUM
import Plutarch.Extra.TermCont (pguardC, pletC) import Plutarch.Extra.Maybe (pfromJust)
import Plutarch.Extra.TermCont (pguardC, pletC, pmatchC)
import Plutarch.Lift ( import Plutarch.Lift (
DerivePConstantViaNewtype (..), DerivePConstantViaNewtype (..),
PConstantDecl, PConstantDecl,
PUnsafeLiftDecl (..), PUnsafeLiftDecl (..),
) )
import Plutarch.SafeMoney (PDiscrete) import Plutarch.SafeMoney (PDiscrete (..))
import Plutarch.Show (PShow (..))
import PlutusLedgerApi.V1 (DatumHash, PubKeyHash, ValidatorHash) import PlutusLedgerApi.V1 (DatumHash, PubKeyHash, ValidatorHash)
import PlutusLedgerApi.V1.Value (AssetClass)
import PlutusTx qualified import PlutusTx qualified
import PlutusTx.AssocMap qualified as AssocMap import PlutusTx.AssocMap qualified as AssocMap
@ -78,6 +92,14 @@ import PlutusTx.AssocMap qualified as AssocMap
@since 0.1.0 @since 0.1.0
-} -}
newtype ProposalId = ProposalId {proposalTag :: Integer} newtype ProposalId = ProposalId {proposalTag :: Integer}
deriving stock
( -- | @since 0.1.0
Eq
, -- | @since 0.1.0
Show
, -- | @since 0.1.0
Generic
)
deriving newtype deriving newtype
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusTx.ToData PlutusTx.ToData
@ -86,14 +108,6 @@ newtype ProposalId = ProposalId {proposalTag :: Integer}
, -- | @since 0.1.0 , -- | @since 0.1.0
PlutusTx.UnsafeFromData PlutusTx.UnsafeFromData
) )
deriving stock
( -- | @since 0.1.0
Eq
, -- | @since 0.1.0
Show
, -- | @since 0.1.0
GHC.Generic
)
{- | Encodes a result. Typically, for a Yes/No proposal, we encode it like this: {- | Encodes a result. Typically, for a Yes/No proposal, we encode it like this:
@ -113,7 +127,7 @@ newtype ResultTag = ResultTag {getResultTag :: Integer}
, -- | @since 0.1.0 , -- | @since 0.1.0
Ord Ord
, -- | @since 0.1.0 , -- | @since 0.1.0
GHC.Generic Generic
) )
deriving newtype deriving newtype
( -- | @since 0.1.0 ( -- | @since 0.1.0
@ -172,11 +186,25 @@ data ProposalStatus
, -- | @since 0.1.0 , -- | @since 0.1.0
Show Show
, -- | @since 0.1.0 , -- | @since 0.1.0
GHC.Generic Generic
, -- | @since 0.2.0
Enum
, -- | @since 0.2.0
Bounded
) )
deriving anyclass
-- | @since 0.1.0 ( -- | @since 0.2.0
PlutusTx.makeIsDataIndexed ''ProposalStatus [('Draft, 0), ('VotingReady, 1), ('Locked, 2), ('Finished, 3)] SOP.Generic
)
deriving
( -- | @since 0.1.0
PlutusTx.FromData
, -- | @since 0.1.0
PlutusTx.ToData
, -- | @since 0.1.0
PlutusTx.UnsafeFromData
)
via (EnumIsData ProposalStatus)
{- | The threshold values for various state transitions to happen. {- | The threshold values for various state transitions to happen.
This data is stored centrally (in the 'Agora.Governor.Governor') and copied over This data is stored centrally (in the 'Agora.Governor.Governor') and copied over
@ -202,11 +230,10 @@ data ProposalThresholds = ProposalThresholds
, -- | @since 0.1.0 , -- | @since 0.1.0
Show Show
, -- | @since 0.1.0 , -- | @since 0.1.0
GHC.Generic Generic
) )
-- | @since 0.1.0 PlutusTx.makeIsDataIndexed 'ProposalThresholds [('ProposalThresholds, 0)]
PlutusTx.makeIsDataIndexed ''ProposalThresholds [('ProposalThresholds, 0)]
{- | Map which encodes the total tally for each result. {- | Map which encodes the total tally for each result.
It's important that the "shape" is consistent with the shape of 'effects'. It's important that the "shape" is consistent with the shape of 'effects'.
@ -224,21 +251,19 @@ PlutusTx.makeIsDataIndexed ''ProposalThresholds [('ProposalThresholds, 0)]
newtype ProposalVotes = ProposalVotes newtype ProposalVotes = ProposalVotes
{ getProposalVotes :: AssocMap.Map ResultTag Integer { getProposalVotes :: AssocMap.Map ResultTag Integer
} }
deriving newtype
( -- | @since 0.1.0
PlutusTx.ToData
, -- | @since 0.1.0
PlutusTx.FromData
, -- | @since 0.1.0
PlutusTx.UnsafeFromData
)
deriving stock deriving stock
( -- | @since 0.1.0 ( -- | @since 0.1.0
Eq Eq
, -- | @since 0.1.0 , -- | @since 0.1.0
Show Show
, -- | @since 0.1.0 , -- | @since 0.1.0
GHC.Generic Generic
)
deriving newtype
( -- | @since 0.1.0
PlutusTx.ToData
, -- | @since 0.1.0
PlutusTx.FromData
) )
{- | Create a 'ProposalVotes' that has the same shape as the 'effects' field. {- | Create a 'ProposalVotes' that has the same shape as the 'effects' field.
@ -264,6 +289,8 @@ data ProposalDatum = ProposalDatum
-- ^ The status the proposal is in. -- ^ The status the proposal is in.
, cosigners :: [PubKeyHash] , cosigners :: [PubKeyHash]
-- ^ Who created the proposal initially, and who cosigned it later. -- ^ Who created the proposal initially, and who cosigned it later.
--
-- This list should be sorted in **ascending** order.
, thresholds :: ProposalThresholds , thresholds :: ProposalThresholds
-- ^ Thresholds copied over on initialization. -- ^ Thresholds copied over on initialization.
, votes :: ProposalVotes , votes :: ProposalVotes
@ -279,10 +306,19 @@ data ProposalDatum = ProposalDatum
, -- | @since 0.1.0 , -- | @since 0.1.0
Show Show
, -- | @since 0.1.0 , -- | @since 0.1.0
GHC.Generic Generic
) )
deriving anyclass
PlutusTx.makeIsDataIndexed ''ProposalDatum [('ProposalDatum, 0)] ( -- | @since 0.2.0
SOP.Generic
)
deriving
( -- | @since 0.1.0
PlutusTx.ToData
, -- | @since 0.1.0
PlutusTx.FromData
)
via (ProductIsData ProposalDatum)
{- | Haskell-level redeemer for Proposal scripts. {- | Haskell-level redeemer for Proposal scripts.
@ -296,22 +332,24 @@ data ProposalRedeemer
-- --
-- This is particularly used in the 'Draft' 'ProposalStatus', -- This is particularly used in the 'Draft' 'ProposalStatus',
-- where matching 'Agora.Stake.Stake's can be called to advance the proposal, -- where matching 'Agora.Stake.Stake's can be called to advance the proposal,
-- provided enough GT is shared among them. -- provided enough GT is shared among them.
--
-- This list should be sorted in ascending order.
Cosign [PubKeyHash] Cosign [PubKeyHash]
| -- | Allow unlocking one or more stakes with votes towards particular 'ResultTag'. | -- | Allow unlocking one or more stakes with votes towards particular 'ResultTag'.
Unlock ResultTag Unlock
| -- | Advance the proposal, performing the required checks for whether that is legal. | -- | Advance the proposal, performing the required checks for whether that is legal.
-- --
-- These are roughly the checks for each possible transition: -- These are roughly the checks for each possible transition:
-- --
-- === @'Draft' -> 'VotingReady'@: -- === @'Draft' -> 'VotingReady'@:
-- --
-- 1. The sum of all of the cosigner's GT is larger than the 'startVoting' field of 'ProposalThresholds'. -- 1. The sum of all of the cosigner's GT is larger than the 'vote' field of 'ProposalThresholds'.
-- 2. The proposal's current time ensures 'isDraftPeriod'. -- 2. The proposal's current time ensures 'isDraftPeriod'.
-- --
-- === @'VotingReady' -> 'Locked'@: -- === @'VotingReady' -> 'Locked'@:
-- --
-- 1. The sum of all votes is larger than 'countVoting'. -- 1. The sum of all votes is larger than 'execute'.
-- 2. The winning 'ResultTag' has more votes than all other 'ResultTag's. -- 2. The winning 'ResultTag' has more votes than all other 'ResultTag's.
-- 3. The proposal's current time ensures 'isVotingPeriod'. -- 3. The proposal's current time ensures 'isVotingPeriod'.
-- --
@ -331,7 +369,7 @@ data ProposalRedeemer
, -- | @since 0.1.0 , -- | @since 0.1.0
Show Show
, -- | @since 0.1.0 , -- | @since 0.1.0
GHC.Generic Generic
) )
-- | @since 0.1.0 -- | @since 0.1.0
@ -343,25 +381,6 @@ PlutusTx.makeIsDataIndexed
, ('AdvanceProposal, 3) , ('AdvanceProposal, 3)
] ]
{- | Parameters that identify the Proposal validator script.
@since 0.1.0
-}
data Proposal = Proposal
{ governorSTAssetClass :: AssetClass
, stakeSTAssetClass :: AssetClass
, maximumCosigners :: Integer
-- ^ Arbitrary limit for maximum amount of cosigners on a proposal.
}
deriving stock
( -- | @since 0.1.0
Show
, -- | @since 0.1.0
Eq
, -- | @since 0.1.0
GHC.Generic
)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Plutarch-land -- Plutarch-land
@ -370,17 +389,31 @@ data Proposal = Proposal
@since 0.1.0 @since 0.1.0
-} -}
newtype PResultTag (s :: S) = PResultTag (Term s PInteger) newtype PResultTag (s :: S) = PResultTag (Term s PInteger)
deriving deriving stock
( -- | @since 0.1.0 ( -- | @since 0.2.0
Generic
)
deriving anyclass
( -- @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
PIsData PIsData
, -- | @since 0.1.0 , -- | @since 0.1.0
PEq PEq
, -- | @since 0.2.0
PPartialOrd
, -- | @since 0.1.0 , -- | @since 0.1.0
POrd POrd
, -- | @since 0.2.0
PShow
) )
via (DerivePNewtype PResultTag PInteger)
-- | @since 0.2.0
instance DerivePlutusType PResultTag where
type DPTStrat _ = PlutusTypeNewtype
-- | @since 0.1.0
instance PTryFrom PData (PAsData PResultTag)
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PResultTag where type PLifted PResultTag = ResultTag instance PUnsafeLiftDecl PResultTag where type PLifted PResultTag = ResultTag
@ -391,34 +424,36 @@ deriving via
instance instance
(PConstantDecl ResultTag) (PConstantDecl ResultTag)
-- | @since 0.1.0
deriving via
PAsData (DerivePNewtype PResultTag PInteger)
instance
PTryFrom PData (PAsData PResultTag)
{- | Plutarch-level version of 'PProposalId'. {- | Plutarch-level version of 'PProposalId'.
@since 0.1.0 @since 0.1.0
-} -}
newtype PProposalId (s :: S) = PProposalId (Term s PInteger) newtype PProposalId (s :: S) = PProposalId (Term s PInteger)
deriving deriving stock
( -- | @since 0.2.0
Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
PIsData PIsData
, -- | @since 0.1.0 , -- | @since 0.1.0
PEq PEq
, -- | @since 0.2.0
PPartialOrd
, -- | @since 0.1.0 , -- | @since 0.1.0
POrd POrd
, -- | @since 0.2.0
PShow
) )
via (DerivePNewtype PProposalId PInteger)
-- | @since 0.2.0
instance DerivePlutusType PProposalId where
type DPTStrat _ = PlutusTypeNewtype
-- | @since 0.1.0 -- | @since 0.1.0
deriving via instance PTryFrom PData (PAsData PProposalId)
PAsData (DerivePNewtype PProposalId PInteger)
instance
PTryFrom PData (PAsData PProposalId)
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PProposalId where type PLifted PProposalId = ProposalId instance PUnsafeLiftDecl PProposalId where type PLifted PProposalId = ProposalId
@ -434,25 +469,23 @@ deriving via
@since 0.1.0 @since 0.1.0
-} -}
data PProposalStatus (s :: S) data PProposalStatus (s :: S)
= -- TODO: 'PProposalStatus' ought te be encoded as 'PInteger'. = -- | @since 0.2.0
-- e.g. like Tilde used 'pmatchEnum'. PDraft
PDraft (Term s (PDataRecord '[])) | -- | @since 0.2.0
| PVotingReady (Term s (PDataRecord '[])) PVoting
| PLocked (Term s (PDataRecord '[])) | -- | @since 0.2.0
| PFinished (Term s (PDataRecord '[])) PLocked
| -- | @since 0.2.0
PFinished
deriving stock deriving stock
( -- | @since 0.1.0
GHC.Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
Generic Generic
, -- | @since 0.2.0
Bounded
, -- | @since 0.2.0
Enum
) )
deriving anyclass deriving anyclass
( -- | @since 0.1.0
PIsDataRepr
)
deriving
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
@ -460,16 +493,19 @@ data PProposalStatus (s :: S)
, -- | @since 0.1.0 , -- | @since 0.1.0
PEq PEq
) )
via PIsDataReprInstances PProposalStatus
-- | @since 0.2.0
instance DerivePlutusType PProposalStatus where
type DPTStrat _ = PlutusTypeEnumData
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PProposalStatus where type PLifted PProposalStatus = ProposalStatus instance PUnsafeLiftDecl PProposalStatus where type PLifted PProposalStatus = ProposalStatus
-- | @since 0.1.0 -- | @since 0.1.0
deriving via PAsData (PIsDataReprInstances PProposalStatus) instance PTryFrom PData (PAsData PProposalStatus) instance PTryFrom PData (PAsData PProposalStatus)
-- | @since 0.1.0 -- | @since 0.1.0
deriving via (DerivePConstantViaData ProposalStatus PProposalStatus) instance (PConstantDecl ProposalStatus) deriving via (DerivePConstantViaEnum ProposalStatus PProposalStatus) instance (PConstantDecl ProposalStatus)
{- | Plutarch-level version of 'ProposalThresholds'. {- | Plutarch-level version of 'ProposalThresholds'.
@ -487,18 +523,10 @@ newtype PProposalThresholds (s :: S) = PProposalThresholds
) )
} }
deriving stock deriving stock
( -- | @since 0.1.0
GHC.Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
Generic Generic
) )
deriving anyclass deriving anyclass
( -- | @since 0.1.0
PIsDataRepr
)
deriving
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
@ -506,13 +534,13 @@ newtype PProposalThresholds (s :: S) = PProposalThresholds
, -- | @since 0.1.0 , -- | @since 0.1.0
PDataFields PDataFields
) )
via (PIsDataReprInstances PProposalThresholds)
-- | @since 0.2.0
instance DerivePlutusType PProposalThresholds where
type DPTStrat _ = PlutusTypeData
-- | @since 0.1.0 -- | @since 0.1.0
deriving via instance PTryFrom PData PProposalThresholds
PAsData (PIsDataReprInstances PProposalThresholds)
instance
PTryFrom PData (PAsData PProposalThresholds)
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PProposalThresholds where type PLifted PProposalThresholds = ProposalThresholds instance PUnsafeLiftDecl PProposalThresholds where type PLifted PProposalThresholds = ProposalThresholds
@ -529,40 +557,23 @@ deriving via
-} -}
newtype PProposalVotes (s :: S) newtype PProposalVotes (s :: S)
= PProposalVotes (Term s (PMap 'Unsorted PResultTag PInteger)) = PProposalVotes (Term s (PMap 'Unsorted PResultTag PInteger))
deriving deriving stock
( -- | @since 0.2.0
Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
PIsData PIsData
) )
via (DerivePNewtype PProposalVotes (PMap 'Unsorted PResultTag PInteger))
-- | @since 0.2.0
instance DerivePlutusType PProposalVotes where
type DPTStrat _ = PlutusTypeNewtype
-- | @since 0.1.0 -- | @since 0.1.0
deriving via instance PTryFrom PData (PAsData PProposalVotes)
PAsData (DerivePNewtype PProposalVotes (PMap 'Unsorted PResultTag PInteger))
instance
PTryFrom PData (PAsData PProposalVotes)
{- | Retract votes given the option and the amount of votes.
@since 0.1.0
-}
pretractVotes :: Term s (PResultTag :--> PInteger :--> PProposalVotes :--> PProposalVotes)
pretractVotes = phoistAcyclic $
plam $ \rt count votes ->
let voteMap :: Term _ (PMap 'Unsorted PResultTag PInteger)
voteMap = pto votes
in pcon $
PProposalVotes $
PM.pupdate
# plam
( \oldCount -> unTermCont $ do
newCount <- pletC $ oldCount - count
pguardC "Resulting vote count greater or equal to 0" $ 0 #<= newCount
pure $ pcon $ PJust newCount
)
# rt
# voteMap
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PProposalVotes where type PLifted PProposalVotes = ProposalVotes instance PUnsafeLiftDecl PProposalVotes where type PLifted PProposalVotes = ProposalVotes
@ -573,19 +584,6 @@ deriving via
instance instance
(PConstantDecl ProposalVotes) (PConstantDecl ProposalVotes)
{- | Plutarch-level version of 'emptyVotesFor'.
@since 0.1.0
-}
pemptyVotesFor :: forall s a. (PIsData a) => Term s (PMap 'Unsorted PResultTag a :--> PProposalVotes)
pemptyVotesFor =
phoistAcyclic $
plam
( \m ->
pcon $
PProposalVotes $ PM.pmap # plam (const $ pconstant 0) # m
)
{- | Plutarch-level version of 'ProposalDatum'. {- | Plutarch-level version of 'ProposalDatum'.
@since 0.1.0 @since 0.1.0
@ -607,37 +605,29 @@ newtype PProposalDatum (s :: S) = PProposalDatum
) )
} }
deriving stock deriving stock
( -- | @since 0.1.0
GHC.Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
Generic Generic
) )
deriving anyclass deriving anyclass
( -- | @since 0.1.0
PIsDataRepr
)
deriving
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
PIsData PIsData
, -- | @since 0.1.0
PDataFields
, -- | @since 0.1.0 , -- | @since 0.1.0
PEq PEq
) )
via (PIsDataReprInstances PProposalDatum)
-- | @since 0.1.0 -- | @since 0.2.0
deriving via PAsData (PIsDataReprInstances PProposalDatum) instance PTryFrom PData (PAsData PProposalDatum) instance DerivePlutusType PProposalDatum where
type DPTStrat _ = PlutusTypeNewtype
instance PTryFrom PData (PAsData PProposalDatum)
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PProposalDatum where type PLifted PProposalDatum = ProposalDatum instance PUnsafeLiftDecl PProposalDatum where type PLifted PProposalDatum = ProposalDatum
-- | @since 0.1.0 -- | @since 0.1.0
deriving via (DerivePConstantViaData ProposalDatum PProposalDatum) instance (PConstantDecl ProposalDatum) deriving via (DerivePConstantViaDataList ProposalDatum PProposalDatum) instance (PConstantDecl ProposalDatum)
{- | Plutarch-level version of 'ProposalRedeemer'. {- | Plutarch-level version of 'ProposalRedeemer'.
@ -646,33 +636,25 @@ deriving via (DerivePConstantViaData ProposalDatum PProposalDatum) instance (PCo
data PProposalRedeemer (s :: S) data PProposalRedeemer (s :: S)
= PVote (Term s (PDataRecord '["resultTag" ':= PResultTag])) = PVote (Term s (PDataRecord '["resultTag" ':= PResultTag]))
| PCosign (Term s (PDataRecord '["newCosigners" ':= PBuiltinList (PAsData PPubKeyHash)])) | PCosign (Term s (PDataRecord '["newCosigners" ':= PBuiltinList (PAsData PPubKeyHash)]))
| PUnlock (Term s (PDataRecord '["resultTag" ':= PResultTag])) | PUnlock (Term s (PDataRecord '[]))
| PAdvanceProposal (Term s (PDataRecord '[])) | PAdvanceProposal (Term s (PDataRecord '[]))
deriving stock deriving stock
( -- | @since 0.1.0
GHC.Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
Generic Generic
) )
deriving anyclass deriving anyclass
( -- | @since 0.1.0
PIsDataRepr
)
deriving
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
PIsData PIsData
) )
via PIsDataReprInstances PProposalRedeemer
-- | @since 0.2.0
instance DerivePlutusType PProposalRedeemer where
type DPTStrat _ = PlutusTypeData
-- | @since 0.1.0 -- | @since 0.1.0
deriving via instance PTryFrom PData PProposalRedeemer
PAsData (PIsDataReprInstances PProposalRedeemer)
instance
PTryFrom PData (PAsData PProposalRedeemer)
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PProposalRedeemer where type PLifted PProposalRedeemer = ProposalRedeemer instance PUnsafeLiftDecl PProposalRedeemer where type PLifted PProposalRedeemer = ProposalRedeemer
@ -688,27 +670,50 @@ deriving via (DerivePConstantViaData ProposalRedeemer PProposalRedeemer) instanc
@since 0.1.0 @since 0.1.0
-} -}
proposalDatumValid :: Proposal -> Term s (Agora.Proposal.PProposalDatum :--> PBool)
proposalDatumValid proposal =
phoistAcyclic $
plam $ \datum' -> unTermCont $ do
datum <- tcont $ pletFields @'["effects", "cosigners", "votes"] $ datum'
let atLeastOneNegativeResult = {- | Return true if the effect list contains at least one neutral outcome.
pany
# phoistAcyclic
(plam $ \m -> pnull #$ pto $ pfromData $ psndBuiltin # m)
#$ pto
$ pfromData datum.effects
pure $ @since 0.2.0
foldr1 -}
(#&&) phasNeutralEffect ::
[ ptraceIfFalse "Proposal has at least one ResultTag has no effects" atLeastOneNegativeResult forall (s :: S).
, ptraceIfFalse "Proposal has at least one cosigner" $ pnotNull # pfromData datum.cosigners Term
, ptraceIfFalse "Proposal has fewer cosigners than the limit" $ plength # pfromData datum.cosigners #<= pconstant proposal.maximumCosigners s
, ptraceIfFalse "Proposal votes and effects are compatible with each other" $ PUM.pkeysEqual # datum.effects # pto (pfromData datum.votes) ( PMap 'Unsorted PResultTag (PMap 'Unsorted PValidatorHash PDatumHash)
] :--> PBool
)
phasNeutralEffect = phoistAcyclic $ PAssocMap.pany # PAssocMap.pnull
{- | Return true if votes and effects of the proposal have the same key set.
@since 0.2.0
-}
pisEffectsVotesCompatible ::
forall (s :: S).
Term
s
( PMap 'Unsorted PResultTag (PMap 'Unsorted PValidatorHash PDatumHash)
:--> PProposalVotes
:--> PBool
)
pisEffectsVotesCompatible = phoistAcyclic $
plam $ \m (pto -> v :: Term _ (PMap _ _ _)) ->
PUM.pkeysEqual # m # v
{- | Retutns true if vote counts of /all/ the options are zero.
@since 0.2.0
-}
pisVotesEmpty ::
forall (s :: S).
Term
s
( PProposalVotes
:--> PBool
)
pisVotesEmpty = phoistAcyclic $
plam $ \(pto -> m :: Term _ (PMap _ _ _)) ->
PAssocMap.pall # plam (#== 0) # m
{- | Wrapper for 'pwinner''. When the winner cannot be found, {- | Wrapper for 'pwinner''. When the winner cannot be found,
the 'neutral' option will be returned. the 'neutral' option will be returned.
@ -793,13 +798,6 @@ phighestVotes = phoistAcyclic $
let l :: Term _ (PBuiltinList _) let l :: Term _ (PBuiltinList _)
l = pto $ pto votes l = pto $ pto votes
f ::
Term
_
( PBuiltinPair (PAsData PResultTag) (PAsData PInteger)
:--> PBuiltinPair (PAsData PResultTag) (PAsData PInteger)
:--> PBuiltinPair (PAsData PResultTag) (PAsData PInteger)
)
f = phoistAcyclic $ f = phoistAcyclic $
plam $ \this last -> plam $ \this last ->
let lastVotes = pfromData $ psndBuiltin # last let lastVotes = pfromData $ psndBuiltin # last
@ -822,10 +820,57 @@ pneutralOption = phoistAcyclic $
let l :: Term _ (PBuiltinList (PBuiltinPair (PAsData PResultTag) _)) let l :: Term _ (PBuiltinList (PBuiltinPair (PAsData PResultTag) _))
l = pto effects l = pto effects
f :: Term _ (PBuiltinPair (PAsData PResultTag) (PAsData (PMap 'Unsorted _ _)) :--> PBool)
f = phoistAcyclic $ f = phoistAcyclic $
plam $ \((pfromData . (psndBuiltin #) -> el)) -> plam $
let el' :: Term _ (PBuiltinList _) pbuiltinUncurry $ \rt el ->
el' = pto el pif
in pnull # el' (PAssocMap.pnull # el)
in pfromData $ pfstBuiltin #$ mustBePJust # "No neutral option" #$ pfind # f # l (pcon $ PJust rt)
(pcon PNothing)
in pfromJust #$ pfirstJust # f # l
{- | Return true if the thresholds are valid.
@since 0.2.0
-}
pisProposalThresholdsValid :: forall (s :: S). Term s (PProposalThresholds :--> PBool)
pisProposalThresholdsValid = phoistAcyclic $
plam $ \thresholds -> unTermCont $ do
thresholdsF <- pletAllC thresholds
PDiscrete execute' <- pmatchC thresholdsF.execute
PDiscrete draft' <- pmatchC thresholdsF.create
PDiscrete vote' <- pmatchC thresholdsF.vote
execute <- pletC $ pextract # execute'
draft <- pletC $ pextract # draft'
vote <- pletC $ pextract # vote'
pure $
foldr1
(#&&)
[ ptraceIfFalse "Execute threshold is less than or equal to 0" $ 0 #<= execute
, ptraceIfFalse "Draft threshold is less than or equal to 0" $ 0 #<= draft
, ptraceIfFalse "Vote threshold is less than or equal to 0" $ 0 #<= vote
]
{- | Retract votes given the option and the amount of votes.
@since 0.1.0
-}
pretractVotes :: Term s (PResultTag :--> PInteger :--> PProposalVotes :--> PProposalVotes)
pretractVotes = phoistAcyclic $
plam $ \rt count votes ->
let voteMap :: Term _ (PMap 'Unsorted PResultTag PInteger)
voteMap = pto votes
in pcon $
PProposalVotes $
PM.pupdate
# plam
( \oldCount -> unTermCont $ do
newCount <- pletC $ oldCount - count
pguardC "Resulting vote count greater or equal to 0" $ 0 #<= newCount
pure $ pcon $ PJust newCount
)
# rt
# voteMap

File diff suppressed because it is too large Load diff

View file

@ -26,11 +26,11 @@ module Agora.Proposal.Time (
isVotingPeriod, isVotingPeriod,
isLockingPeriod, isLockingPeriod,
isExecutionPeriod, isExecutionPeriod,
pisProposalTimingConfigValid,
pisMaxTimeRangeWidthValid,
) where ) where
import Agora.Plutarch.Orphans () import Control.Composition ((.*))
import GHC.Generics qualified as GHC
import Generics.SOP (Generic, HasDatatypeInfo, I (I))
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
PExtended (PFinite), PExtended (PFinite),
PInterval (PInterval), PInterval (PInterval),
@ -42,18 +42,19 @@ import Plutarch.Api.V1 (
import Plutarch.DataRepr ( import Plutarch.DataRepr (
DerivePConstantViaData (..), DerivePConstantViaData (..),
PDataFields, PDataFields,
PIsDataReprInstances (..),
) )
import Plutarch.Extra.TermCont (pguardC, pmatchC) import Plutarch.Extra.Applicative (PApply (pliftA2))
import Plutarch.Extra.Field (pletAll, pletAllC)
import Plutarch.Extra.Maybe (pjust, pmaybe, pnothing)
import Plutarch.Extra.TermCont (pmatchC)
import Plutarch.Lift ( import Plutarch.Lift (
DerivePConstantViaNewtype (..), DerivePConstantViaNewtype (..),
PConstantDecl, PConstantDecl,
PUnsafeLiftDecl (..), PUnsafeLiftDecl (..),
) )
import Plutarch.Numeric.Additive (AdditiveSemigroup ((+))) import PlutusLedgerApi.V1 (POSIXTime)
import PlutusLedgerApi.V1.Time (POSIXTime)
import PlutusTx qualified import PlutusTx qualified
import Prelude hiding ((+)) import Prelude
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -64,8 +65,22 @@ import Prelude hiding ((+))
newtype ProposalStartingTime = ProposalStartingTime newtype ProposalStartingTime = ProposalStartingTime
{ getProposalStartingTime :: POSIXTime { getProposalStartingTime :: POSIXTime
} }
deriving newtype (PlutusTx.ToData, PlutusTx.FromData, PlutusTx.UnsafeFromData) deriving stock
deriving stock (Eq, Show, GHC.Generic) ( -- | @since 0.1.0
Eq
, -- | @since 0.1.0
Show
, -- | @since 0.1.0
Generic
)
deriving newtype
( -- | @since 0.1.0
PlutusTx.ToData
, -- | @since 0.1.0
PlutusTx.FromData
, -- | @since 0.1.0
PlutusTx.UnsafeFromData
)
{- | Configuration of proposal timings. {- | Configuration of proposal timings.
@ -89,11 +104,10 @@ data ProposalTimingConfig = ProposalTimingConfig
, -- | @since 0.1.0 , -- | @since 0.1.0
Show Show
, -- | @since 0.1.0 , -- | @since 0.1.0
GHC.Generic Generic
) )
-- | @since 0.1.0 PlutusTx.makeIsDataIndexed 'ProposalTimingConfig [('ProposalTimingConfig, 0)]
PlutusTx.makeIsDataIndexed ''ProposalTimingConfig [('ProposalTimingConfig, 0)]
-- | Represents the maximum width of a 'PlutusLedgerApi.V1.Time.POSIXTimeRange'. -- | Represents the maximum width of a 'PlutusLedgerApi.V1.Time.POSIXTimeRange'.
newtype MaxTimeRangeWidth = MaxTimeRangeWidth {getMaxWidth :: POSIXTime} newtype MaxTimeRangeWidth = MaxTimeRangeWidth {getMaxWidth :: POSIXTime}
@ -105,7 +119,7 @@ newtype MaxTimeRangeWidth = MaxTimeRangeWidth {getMaxWidth :: POSIXTime}
, -- | @since 0.1.0 , -- | @since 0.1.0
Ord Ord
, -- | @since 0.1.0 , -- | @since 0.1.0
GHC.Generic Generic
) )
deriving newtype deriving newtype
( -- | @since 0.1.0 ( -- | @since 0.1.0
@ -151,41 +165,41 @@ data PProposalTime (s :: S) = PProposalTime
} }
deriving stock deriving stock
( -- | @since 0.1.0 ( -- | @since 0.1.0
GHC.Generic Generic
) )
deriving anyclass deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
Generic
, -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0
HasDatatypeInfo
, -- | @since 0.1.0 , -- | @since 0.1.0
PEq PEq
) )
instance DerivePlutusType PProposalTime where
type DPTStrat _ = PlutusTypeScott
-- | Plutarch-level version of 'ProposalStartingTime'. -- | Plutarch-level version of 'ProposalStartingTime'.
newtype PProposalStartingTime (s :: S) = PProposalStartingTime (Term s PPOSIXTime) newtype PProposalStartingTime (s :: S) = PProposalStartingTime (Term s PPOSIXTime)
deriving deriving stock
( -- | @since 0.1.0
Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
PIsData PIsData
, -- | @since 0.1.0 , -- | @since 0.1.0
PEq PEq
, -- | @since 0.1.0
POrd
) )
via (DerivePNewtype PProposalStartingTime PPOSIXTime)
instance DerivePlutusType PProposalStartingTime where
type DPTStrat _ = PlutusTypeNewtype
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PProposalStartingTime where instance PUnsafeLiftDecl PProposalStartingTime where
type PLifted PProposalStartingTime = ProposalStartingTime type PLifted PProposalStartingTime = ProposalStartingTime
deriving via instance PTryFrom PData (PAsData PProposalStartingTime)
PAsData (DerivePNewtype PProposalStartingTime PPOSIXTime)
instance
PTryFrom PData (PAsData PProposalStartingTime)
-- | @since 0.1.0 -- | @since 0.1.0
deriving via deriving via
@ -210,18 +224,10 @@ newtype PProposalTimingConfig (s :: S) = PProposalTimingConfig
) )
} }
deriving stock deriving stock
( -- | @since 0.1.0
GHC.Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
Generic Generic
) )
deriving anyclass deriving anyclass
( -- | @since 0.1.0
PIsDataRepr
)
deriving
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
@ -229,10 +235,12 @@ newtype PProposalTimingConfig (s :: S) = PProposalTimingConfig
, -- | @since 0.1.0 , -- | @since 0.1.0
PDataFields PDataFields
) )
via (PIsDataReprInstances PProposalTimingConfig)
instance DerivePlutusType PProposalTimingConfig where
type DPTStrat _ = PlutusTypeData
-- | @since 0.1.0 -- | @since 0.1.0
deriving via PAsData (PIsDataReprInstances PProposalTimingConfig) instance PTryFrom PData (PAsData PProposalTimingConfig) instance PTryFrom PData PProposalTimingConfig
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PProposalTimingConfig where instance PUnsafeLiftDecl PProposalTimingConfig where
@ -247,20 +255,28 @@ deriving via
-- | Plutarch-level version of 'MaxTimeRangeWidth'. -- | Plutarch-level version of 'MaxTimeRangeWidth'.
newtype PMaxTimeRangeWidth (s :: S) newtype PMaxTimeRangeWidth (s :: S)
= PMaxTimeRangeWidth (Term s PPOSIXTime) = PMaxTimeRangeWidth (Term s PPOSIXTime)
deriving deriving stock
( -- | @since 0.2.0
Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
PIsData PIsData
, -- | @since 0.1.0 , -- | @since 0.1.0
PEq PEq
, -- | @since 0.2.0
PPartialOrd
, -- | @since 0.1.0 , -- | @since 0.1.0
POrd POrd
) )
via (DerivePNewtype PMaxTimeRangeWidth PPOSIXTime)
instance DerivePlutusType PMaxTimeRangeWidth where
type DPTStrat _ = PlutusTypeNewtype
-- | @since 0.1.0 -- | @since 0.1.0
deriving via PAsData (DerivePNewtype PMaxTimeRangeWidth PPOSIXTime) instance PTryFrom PData (PAsData PMaxTimeRangeWidth) instance PTryFrom PData (PAsData PMaxTimeRangeWidth)
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PMaxTimeRangeWidth where type PLifted PMaxTimeRangeWidth = MaxTimeRangeWidth instance PUnsafeLiftDecl PMaxTimeRangeWidth where type PLifted PMaxTimeRangeWidth = MaxTimeRangeWidth
@ -273,29 +289,76 @@ deriving via
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
{- | Return true if the timing configuration is valid.
@since 0.2.0
-}
pisProposalTimingConfigValid :: Term s (PProposalTimingConfig :--> PBool)
pisProposalTimingConfigValid = phoistAcyclic $
plam $ \conf -> unTermCont $ do
confF <- pletAllC conf
-- everything is greater or equal 0
pure $
ptraceIfFalse "ge 0" $
foldr
( \t ->
(#&&)
( pconstant 0
#<= pfromData t
)
)
(pconstant True)
[ confF.draftTime
, confF.votingTime
, confF.lockingTime
, confF.executingTime
]
{- | Return true if the maximum time width is greater than 0.
@since 0.2.0
-}
pisMaxTimeRangeWidthValid :: Term s (PMaxTimeRangeWidth :--> PBool)
pisMaxTimeRangeWidthValid =
phoistAcyclic $
plam $
ptraceIfFalse "greater than 0"
. (pconstant (MaxTimeRangeWidth 0) #<)
{- | Get the starting time of a proposal, from the 'PlutusLedgerApi.V1.txInfoValidPeriod' field. {- | Get the starting time of a proposal, from the 'PlutusLedgerApi.V1.txInfoValidPeriod' field.
For every proposal, this is only meant to run once upon creation. Given time range should be For every proposal, this is only meant to run once upon creation. Given time range should be
tight enough, meaning that the width of the time range should be less than the maximum value. tight enough, meaning that the width of the time range should be less than the maximum value.
@since 0.1.0 @since 0.1.0
-} -}
createProposalStartingTime :: forall (s :: S). Term s (PMaxTimeRangeWidth :--> PPOSIXTimeRange :--> PProposalStartingTime) createProposalStartingTime ::
forall (s :: S).
Term
s
( PMaxTimeRangeWidth
:--> PPOSIXTimeRange
:--> PMaybe PProposalStartingTime
)
createProposalStartingTime = phoistAcyclic $ createProposalStartingTime = phoistAcyclic $
plam $ \(pto -> maxDuration) iv -> unTermCont $ do plam $ \(pto -> maxDuration) iv ->
currentTimeF <- pmatchC $ currentProposalTime # iv let ct = currentProposalTime # iv
-- Use the middle of the current time range as the starting time. f :: Term _ (PProposalTime :--> PMaybe PProposalStartingTime)
let duration = currentTimeF.upperBound - currentTimeF.lowerBound f = plam $
flip pmatch $ \(PProposalTime lb ub) ->
let duration = ub - lb
startingTime = startingTime = pdiv # (lb + ub) # 2
pdiv in pif
# (currentTimeF.lowerBound + currentTimeF.upperBound) (duration #<= maxDuration)
# 2 (pjust #$ pcon $ PProposalStartingTime startingTime)
( ptrace
pguardC "createProposalStartingTime: given time range should be tight enough" $ "createProposalStartingTime: given time range should be tight enough"
duration #<= maxDuration pnothing
)
pure $ pcon $ PProposalStartingTime startingTime in -- TODO: PMonad when?
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.
@ -304,33 +367,30 @@ createProposalStartingTime = phoistAcyclic $
@since 0.1.0 @since 0.1.0
-} -}
currentProposalTime :: forall (s :: S). Term s (PPOSIXTimeRange :--> PProposalTime) currentProposalTime :: forall (s :: S). Term s (PPOSIXTimeRange :--> PMaybe PProposalTime)
currentProposalTime = phoistAcyclic $ currentProposalTime = phoistAcyclic $
plam $ \iv -> unTermCont $ do plam $ \iv -> unTermCont $ do
PInterval iv' <- pmatchC iv PInterval iv' <- pmatchC iv
ivf <- tcont $ pletFields @'["from", "to"] iv' ivf <- pletAllC iv'
PLowerBound lb <- pmatchC ivf.from PLowerBound lb <- pmatchC ivf.from
PUpperBound ub <- pmatchC ivf.to PUpperBound ub <- pmatchC ivf.to
lbf <- tcont $ pletFields @'["_0", "_1"] lb
ubf <- tcont $ pletFields @'["_0", "_1"] ub let getBound = phoistAcyclic $
pure $ plam $
pcon $ flip pletAll $ \f ->
PProposalTime pif
{ lowerBound = f._1
pmatch ( pmatch f._0 $ \case
lbf._0 PFinite (pfromData . (pfield @"_0" #) -> d) -> pjust # d
( \case _ -> ptrace "currentProposalTime: time range should be bounded" pnothing
PFinite ((pfield @"_0" #) -> d) -> d
_ -> ptraceError "currentProposalTime: Can't get fully-bounded proposal time."
) )
, upperBound = (ptrace "currentProposalTime: time range should be inclusive" pnothing)
pmatch
ubf._0 lowerBound = getBound # lb
( \case upperBound = getBound # ub
PFinite ((pfield @"_0" #) -> d) -> d
_ -> ptraceError "currentProposalTime: Can't get fully-bounded proposal time." mkTime = phoistAcyclic $ plam $ pcon .* PProposalTime
) pure $ pliftA2 # mkTime # lowerBound # upperBound
}
{- | Check if 'PProposalTime' is within two 'PPOSIXTime'. Inclusive. {- | Check if 'PProposalTime' is within two 'PPOSIXTime'. Inclusive.

View file

@ -1,99 +0,0 @@
{- |
Module : Agora.ScriptInfo
Maintainer : emi@haskell.fyi
Description: Exportable script bundles for off-chain consumption.
Exportable script bundles for off-chain consumption.
-}
module Agora.ScriptInfo (
-- * Types
PolicyInfo (..),
ValidatorInfo (..),
-- * Introduction functions
mkValidatorInfo,
mkPolicyInfo,
) where
import Agora.Aeson.Orphans ()
import Data.Aeson qualified as Aeson
import GHC.Generics qualified as GHC
import Plutarch.Api.V1 (PMintingPolicy, PValidator, mintingPolicySymbol, mkMintingPolicy, mkValidator, validatorHash)
import PlutusLedgerApi.V1 (MintingPolicy, Validator, ValidatorHash)
import PlutusLedgerApi.V1.Value (CurrencySymbol)
{- | Bundle containing a 'Validator' and its hash.
@since 0.1.0
-}
data ValidatorInfo = ValidatorInfo
{ script :: Validator
-- ^ The validator script.
, hash :: ValidatorHash
-- ^ Hash of the validator.
}
deriving stock
( -- | @since 0.1.0
Show
, -- | @since 0.1.0
Eq
, -- | @since 0.1.0
GHC.Generic
)
deriving anyclass
( -- | @since 0.1.0
Aeson.ToJSON
, -- | @since 0.1.0
Aeson.FromJSON
)
{- | Create a 'ValidatorInfo' given a Plutarch term.
@since 0.1.0
-}
mkValidatorInfo :: ClosedTerm PValidator -> ValidatorInfo
mkValidatorInfo term =
ValidatorInfo
{ script = validator
, hash = validatorHash validator
}
where
validator = mkValidator term
{- | Bundle containing a 'MintingPolicy' and its symbol.
@since 0.1.0
-}
data PolicyInfo = PolicyInfo
{ policy :: MintingPolicy
-- ^ The minting policy.
, currencySymbol :: CurrencySymbol
-- ^ The symbol given by the minting policy.
}
deriving stock
( -- | @since 0.1.0
Show
, -- | @since 0.1.0
Eq
, -- | @since 0.1.0
GHC.Generic
)
deriving anyclass
( -- | @since 0.1.0
Aeson.ToJSON
, -- | @since 0.1.0
Aeson.FromJSON
)
{- | Create a 'PolicyInfo' given a Plutarch term.
@since 0.1.0
-}
mkPolicyInfo :: ClosedTerm PMintingPolicy -> PolicyInfo
mkPolicyInfo term =
PolicyInfo
{ policy = policy
, currencySymbol = mintingPolicySymbol policy
}
where
policy = mkMintingPolicy term

138
agora/Agora/Scripts.hs Normal file
View file

@ -0,0 +1,138 @@
{- | Module : Agora.Scripts
Maintainer : connor@mlabs.city
Description: Precompiled core scripts and utilities
Precompiled core scripts and utilities
-}
module Agora.Scripts (
AgoraScripts (..),
governorSTSymbol,
governorSTAssetClass,
governorValidatorHash,
proposalSTSymbol,
proposalSTAssetClass,
proposalValidatoHash,
stakeSTSymbol,
stakeSTAssetClass,
stakeValidatorHash,
authorityTokenSymbol,
treasuryValidatorHash,
) where
import Agora.Governor (GovernorDatum, GovernorRedeemer)
import Agora.Proposal (ProposalDatum, ProposalRedeemer)
import Agora.Stake (StakeDatum, StakeRedeemer)
import Agora.Treasury (TreasuryRedeemer)
import Agora.Utils (CompiledMintingPolicy (..), CompiledValidator (..), validatorHashToTokenName)
import Plutarch.Api.V1 (mintingPolicySymbol, validatorHash)
import PlutusLedgerApi.V1 (CurrencySymbol)
import PlutusLedgerApi.V1.Scripts (ValidatorHash)
import PlutusLedgerApi.V1.Value (AssetClass (..))
{- | Precompiled core scripts.
Including:
- Governor policy
- Governor validator
- Proposal policy
- Proposal validator
- Stake policy
- Stake validator
- Treasury validator
- Authority token policy
@since 0.2.0
-}
data AgoraScripts = AgoraScripts
{ compiledGovernorPolicy :: CompiledMintingPolicy ()
, compiledGovernorValidator :: CompiledValidator GovernorDatum GovernorRedeemer
, compiledStakePolicy :: CompiledMintingPolicy ()
, compiledStakeValidator :: CompiledValidator StakeDatum StakeRedeemer
, compiledProposalPolicy :: CompiledMintingPolicy ()
, compiledProposalValidator :: CompiledValidator ProposalDatum ProposalRedeemer
, compiledTreasuryValidator :: CompiledValidator () TreasuryRedeemer
, compiledAuthorityTokenPolicy :: CompiledMintingPolicy ()
}
{- | Get the currency symbol of the governor state token.
@since 0.2.0
-}
governorSTSymbol :: AgoraScripts -> CurrencySymbol
governorSTSymbol = mintingPolicySymbol . getCompiledMintingPolicy . compiledGovernorPolicy
{- | Get the asset class of the governor state token.
@since 0.2.0
-}
governorSTAssetClass :: AgoraScripts -> AssetClass
governorSTAssetClass as = AssetClass (governorSTSymbol as, "")
{- | Get the script hash of the governor validator.
@since 0.2.0
-}
governorValidatorHash :: AgoraScripts -> ValidatorHash
governorValidatorHash = validatorHash . getCompiledValidator . compiledGovernorValidator
{- | Get the currency symbol of the propsoal state token.
@since 0.2.0
-}
proposalSTSymbol :: AgoraScripts -> CurrencySymbol
proposalSTSymbol as = mintingPolicySymbol $ getCompiledMintingPolicy as.compiledProposalPolicy
{- | Get the asset class of the governor state token.
@since 0.2.0
-}
proposalSTAssetClass :: AgoraScripts -> AssetClass
proposalSTAssetClass as = AssetClass (proposalSTSymbol as, "")
{- | Get the script hash of the proposal validator.
@since 0.2.0
-}
proposalValidatoHash :: AgoraScripts -> ValidatorHash
proposalValidatoHash = validatorHash . getCompiledValidator . compiledProposalValidator
{- | Get the script hash of the governor validator.
@since 0.2.0
-}
stakeSTSymbol :: AgoraScripts -> CurrencySymbol
stakeSTSymbol = mintingPolicySymbol . getCompiledMintingPolicy . compiledStakePolicy
{- | Get the asset class of the stake state token.
Note that this token is tagged with the hash of the stake validator.
See 'Agora.Stake.Script.stakePolicy'.
@since 0.2.0
-}
stakeSTAssetClass :: AgoraScripts -> AssetClass
stakeSTAssetClass as =
let tn = validatorHashToTokenName $ stakeValidatorHash as
in AssetClass (stakeSTSymbol as, tn)
{- | Get the script hash of the stake validator.
@since 0.2.0
-}
stakeValidatorHash :: AgoraScripts -> ValidatorHash
stakeValidatorHash = validatorHash . getCompiledValidator . compiledStakeValidator
{- | Get the currency symbol of the authority token.
@since 0.2.0
-}
authorityTokenSymbol :: AgoraScripts -> CurrencySymbol
authorityTokenSymbol = mintingPolicySymbol . getCompiledMintingPolicy . compiledAuthorityTokenPolicy
{- | Get the script hash of the treasury validator.
@since 0.2.0
-}
treasuryValidatorHash :: AgoraScripts -> ValidatorHash
treasuryValidatorHash = validatorHash . getCompiledValidator . compiledTreasuryValidator

View file

@ -11,71 +11,54 @@ module Agora.Stake (
-- * Haskell-land -- * Haskell-land
StakeDatum (..), StakeDatum (..),
StakeRedeemer (..), StakeRedeemer (..),
Stake (..),
ProposalLock (..), ProposalLock (..),
-- * Plutarch-land -- * Plutarch-land
PStakeDatum (..), PStakeDatum (..),
PStakeRedeemer (..), PStakeRedeemer (..),
PProposalLock (..), PProposalLock (..),
PStakeUsage (..), PStakeRole (..),
-- * Utility functions -- * Utility functions
stakeLocked, pstakeLocked,
findStakeOwnedBy, pnumCreatedProposals,
pgetStakeUsage, pextractVoteOption,
pgetStakeRole,
pisVoter,
pisCreator,
pisPureCreator,
pisIrrelevant,
) where ) where
import Agora.Plutarch.Orphans ()
import Agora.Proposal (PProposalId, PResultTag, ProposalId (..), ResultTag (..)) import Agora.Proposal (PProposalId, PResultTag, ProposalId (..), ResultTag (..))
import Agora.SafeMoney (GTTag) import Agora.SafeMoney (GTTag)
import Data.Tagged (Tagged (..)) import Data.Tagged (Tagged (..))
import GHC.Generics qualified as GHC import Generics.SOP qualified as SOP
import Generics.SOP (Generic, HasDatatypeInfo, I (I))
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
PDatum, PMaybeData,
PDatumHash,
PMaybeData (PDJust, PDNothing),
PPubKeyHash, PPubKeyHash,
PTuple,
PTxInInfo (PTxInInfo),
PTxOut (PTxOut),
) )
import Plutarch.Api.V1.AssetClass (PAssetClass, passetClassValueOf)
import Plutarch.Api.V1.ScriptContext (ptryFindDatum)
import Plutarch.DataRepr ( import Plutarch.DataRepr (
DerivePConstantViaData (..), DerivePConstantViaData (..),
PDataFields,
PIsDataReprInstances (PIsDataReprInstances),
) )
import Plutarch.Extra.List (pmapMaybe, pnotNull) import Plutarch.Extra.Field (pletAll)
import Plutarch.Extra.TermCont (pletC, pletFieldsC, pmatchC) import Plutarch.Extra.IsData (
import Plutarch.Internal (punsafeCoerce) DerivePConstantViaDataList (..),
ProductIsData (ProductIsData),
)
import Plutarch.Extra.List (pnotNull)
import Plutarch.Extra.Sum (PSum (..))
import Plutarch.Extra.Traversable (pfoldMap)
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..)) import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))
import Plutarch.SafeMoney (PDiscrete) import Plutarch.SafeMoney (PDiscrete)
import Plutarch.Show (PShow (..))
import PlutusLedgerApi.V1 (PubKeyHash) import PlutusLedgerApi.V1 (PubKeyHash)
import PlutusLedgerApi.V1.Value (AssetClass)
import PlutusTx qualified import PlutusTx qualified
import Prelude hiding (Num (..)) import Prelude hiding (Num (..))
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
{- | Parameters for creating Stake scripts. {- | Locks that are stored in the stake datums for various purposes.
@since 0.1.0
-}
data Stake = Stake
{ gtClassRef :: Tagged GTTag AssetClass
-- ^ Used when inlining the AssetClass of a 'PDiscrete' in the script code.
, proposalSTClass :: AssetClass
}
deriving stock
( -- | @since 0.1.0
GHC.Generic
)
{- | A lock placed on a Stake datum in order to prevent
depositing and withdrawing when votes are in place.
NOTE: Due to retracting votes always being possible, NOTE: Due to retracting votes always being possible,
this lock will only lock with contention on the proposal. this lock will only lock with contention on the proposal.
@ -102,22 +85,41 @@ data Stake = Stake
@since 0.1.0 @since 0.1.0
-} -}
data ProposalLock = ProposalLock data ProposalLock
{ vote :: ResultTag = -- | The stake was used to create a proposal.
-- ^ What was voted on. This allows retracting votes to --
-- undo their vote. -- This kind of lock is placed upon the creation of a proposal, in order
, proposalId :: ProposalId -- to limit creation of proposals per stake.
-- ^ Identifies the proposal. See 'ProposalId' for further --
-- comments on its significance. -- See also: https://github.com/Liqwid-Labs/agora/issues/68
} --
-- @since 0.2.0
Created
ProposalId
-- ^ The identifier of the proposal.
| -- | The stake was used to vote on a proposal.
--
-- This kind of lock is placed while voting on a proposal, in order to
-- prevent depositing and withdrawing when votes are in place.
--
-- @since 0.2.0
Voted
ProposalId
-- ^ The identifier of the proposal.
ResultTag
-- ^ The option which was voted on. This allows votes to be retracted.
deriving stock deriving stock
( -- | @since 0.1.0 ( -- | @since 0.1.0
Show Show
, -- | @since 0.1.0 , -- | @since 0.1.0
GHC.Generic Generic
) )
PlutusTx.makeIsDataIndexed ''ProposalLock [('ProposalLock, 0)] PlutusTx.makeIsDataIndexed
''ProposalLock
[ ('Created, 0)
, ('Voted, 1)
]
{- | Haskell-level redeemer for Stake scripts. {- | Haskell-level redeemer for Stake scripts.
@ -135,16 +137,26 @@ data StakeRedeemer
-- This needs to be done in sync with casting a vote, otherwise -- This needs to be done in sync with casting a vote, otherwise
-- it's possible for a lock to be permanently placed on the stake, -- it's possible for a lock to be permanently placed on the stake,
-- and then the funds are lost. -- and then the funds are lost.
PermitVote ProposalLock PermitVote
| -- | Retract a vote, removing it from the 'lockedBy' field. See 'ProposalLock'. | -- | Retract a vote, removing it from the 'lockedBy' field. See 'ProposalLock'.
-- This action checks for permission of the 'Agora.Proposal.Proposal'. Finished proposals are -- This action checks for permission of the 'Agora.Proposal.Proposal'. Finished proposals are
-- always allowed to have votes retracted and won't affect the Proposal datum, -- always allowed to have votes retracted and won't affect the Proposal datum,
-- allowing 'Stake's to be unlocked. -- allowing 'Stake's to be unlocked.
RetractVotes [ProposalLock] RetractVotes
| -- | The owner can consume stake if nothing is changed about it. | -- | The owner can consume stake if nothing is changed about it.
-- If the proposal token moves, this is equivalent to the owner consuming it. -- If the proposal token moves, this is equivalent to the owner consuming it.
WitnessStake WitnessStake
deriving stock (Show, GHC.Generic) | -- | The owner can delegate the stake to another user, allowing the
-- delegate to vote on prooposals with the stake.
DelegateTo PubKeyHash
| -- | Revoke the existing delegation.
ClearDelegate
deriving stock
( -- | @since 0.1.0
Show
, -- | @since 0.1.0
Generic
)
PlutusTx.makeIsDataIndexed PlutusTx.makeIsDataIndexed
''StakeRedeemer ''StakeRedeemer
@ -153,6 +165,8 @@ PlutusTx.makeIsDataIndexed
, ('PermitVote, 2) , ('PermitVote, 2)
, ('RetractVotes, 3) , ('RetractVotes, 3)
, ('WitnessStake, 4) , ('WitnessStake, 4)
, ('DelegateTo, 5)
, ('ClearDelegate, 6)
] ]
{- | Haskell-level datum for Stake scripts. {- | Haskell-level datum for Stake scripts.
@ -162,19 +176,35 @@ PlutusTx.makeIsDataIndexed
data StakeDatum = StakeDatum data StakeDatum = StakeDatum
{ stakedAmount :: Tagged GTTag Integer { stakedAmount :: Tagged GTTag Integer
-- ^ Tracks the amount of governance token staked in the datum. -- ^ Tracks the amount of governance token staked in the datum.
-- This also acts as the voting weight for 'Agora.Proposal.Proposal's. -- This also acts as the voting weight for 'Agora.Proposal.Proposal's.
, owner :: PubKeyHash , owner :: PubKeyHash
-- ^ The hash of the public key this stake belongs to. -- ^ The hash of the public key this stake belongs to.
-- --
-- TODO Support for MultiSig/Scripts is tracked here: -- TODO Support for MultiSig/Scripts is tracked here:
-- https://github.com/Liqwid-Labs/agora/issues/45 -- https://github.com/Liqwid-Labs/agora/issues/45
, delegatedTo :: Maybe PubKeyHash
-- ^ To whom this stake has been delegated.
, lockedBy :: [ProposalLock] , lockedBy :: [ProposalLock]
-- ^ The current proposals locking this stake. This field must be empty -- ^ The current proposals locking this stake. This field must be empty
-- for the stake to be usable for deposits and withdrawals. -- for the stake to be usable for deposits and withdrawals.
} }
deriving stock (Show, GHC.Generic) deriving stock
( -- | @since 0.1.0
PlutusTx.makeIsDataIndexed ''StakeDatum [('StakeDatum, 0)] Show
, -- | @since 0.1.0
Generic
)
deriving anyclass
( -- | @since 0.1.0
SOP.Generic
)
deriving
( -- | @since 0.1.0
PlutusTx.ToData
, -- | @since 0.1.0
PlutusTx.FromData
)
via (ProductIsData StakeDatum)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -189,42 +219,39 @@ newtype PStakeDatum (s :: S) = PStakeDatum
( PDataRecord ( PDataRecord
'[ "stakedAmount" ':= PDiscrete GTTag '[ "stakedAmount" ':= PDiscrete GTTag
, "owner" ':= PPubKeyHash , "owner" ':= PPubKeyHash
, "delegatedTo" ':= PMaybeData (PAsData PPubKeyHash)
, "lockedBy" ':= PBuiltinList (PAsData PProposalLock) , "lockedBy" ':= PBuiltinList (PAsData PProposalLock)
] ]
) )
} }
deriving stock deriving stock
( -- | @since 0.1.0
GHC.Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
Generic Generic
) )
deriving anyclass deriving anyclass
( -- | @since 0.1.0
PIsDataRepr
)
deriving
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
PIsData PIsData
, -- | @since 0.1.0
PDataFields
, -- | @since 0.1.0 , -- | @since 0.1.0
PEq PEq
) )
via (PIsDataReprInstances PStakeDatum)
instance DerivePlutusType PStakeDatum where
type DPTStrat _ = PlutusTypeNewtype
-- | @since 0.1.0 -- | @since 0.1.0
instance Plutarch.Lift.PUnsafeLiftDecl PStakeDatum where type PLifted PStakeDatum = StakeDatum instance Plutarch.Lift.PUnsafeLiftDecl PStakeDatum where
type PLifted PStakeDatum = StakeDatum
-- | @since 0.1.0 -- | @since 0.1.0
deriving via (DerivePConstantViaData StakeDatum PStakeDatum) instance (Plutarch.Lift.PConstantDecl StakeDatum) deriving via
(DerivePConstantViaDataList StakeDatum PStakeDatum)
instance
(Plutarch.Lift.PConstantDecl StakeDatum)
-- | @since 0.1.0 -- | @since 0.1.0
deriving via PAsData (PIsDataReprInstances PStakeDatum) instance PTryFrom PData (PAsData PStakeDatum) instance PTryFrom PData (PAsData PStakeDatum)
{- | Plutarch-level redeemer for Stake scripts. {- | Plutarch-level redeemer for Stake scripts.
@ -235,208 +262,257 @@ data PStakeRedeemer (s :: S)
PDepositWithdraw (Term s (PDataRecord '["delta" ':= PDiscrete GTTag])) PDepositWithdraw (Term s (PDataRecord '["delta" ':= PDiscrete GTTag]))
| -- | Destroy a stake, retrieving its LQ, the minimum ADA and any other assets. | -- | Destroy a stake, retrieving its LQ, the minimum ADA and any other assets.
PDestroy (Term s (PDataRecord '[])) PDestroy (Term s (PDataRecord '[]))
| PPermitVote (Term s (PDataRecord '["lock" ':= PProposalLock])) | PPermitVote (Term s (PDataRecord '[]))
| PRetractVotes (Term s (PDataRecord '["locks" ':= PBuiltinList (PAsData PProposalLock)])) | PRetractVotes (Term s (PDataRecord '[]))
| PWitnessStake (Term s (PDataRecord '[])) | PWitnessStake (Term s (PDataRecord '[]))
| PDelegateTo (Term s (PDataRecord '["pkh" ':= PPubKeyHash]))
| PClearDelegate (Term s (PDataRecord '[]))
deriving stock deriving stock
( -- | @since 0.1.0
GHC.Generic
)
deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
Generic Generic
) )
deriving anyclass deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.1.0
PIsDataRepr SOP.Generic
) , -- | @since 0.1.0
deriving
( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
PIsData PIsData
) )
via PIsDataReprInstances PStakeRedeemer
instance DerivePlutusType PStakeRedeemer where
type DPTStrat _ = PlutusTypeData
-- | @since 0.1.0
instance PTryFrom PData PStakeRedeemer
-- | @since 0.1.0
instance Plutarch.Lift.PUnsafeLiftDecl PStakeRedeemer where
type PLifted PStakeRedeemer = StakeRedeemer
-- | @since 0.1.0
deriving via deriving via
PAsData (PIsDataReprInstances PStakeRedeemer) (DerivePConstantViaData StakeRedeemer PStakeRedeemer)
instance instance
PTryFrom PData (PAsData PStakeRedeemer) (Plutarch.Lift.PConstantDecl StakeRedeemer)
instance Plutarch.Lift.PUnsafeLiftDecl PStakeRedeemer where type PLifted PStakeRedeemer = StakeRedeemer
deriving via (DerivePConstantViaData StakeRedeemer PStakeRedeemer) instance (Plutarch.Lift.PConstantDecl StakeRedeemer)
{- | Plutarch-level version of 'ProposalLock'. {- | Plutarch-level version of 'ProposalLock'.
@since 0.1.0 @since 0.2.0
-} -}
newtype PProposalLock (s :: S) = PProposalLock data PProposalLock (s :: S)
{ getProposalLock :: = PCreated
Term ( Term
s s
( PDataRecord ( PDataRecord
'[ "vote" ':= PResultTag '["created" ':= PProposalId]
, "proposalTag" ':= PProposalId )
] )
) | PVoted
} ( Term
deriving stock (GHC.Generic) s
deriving anyclass (Generic) ( PDataRecord
deriving anyclass (PIsDataRepr) '[ "votedOn" ':= PProposalId
deriving , "votedFor" ':= PResultTag
(PlutusType, PIsData, PDataFields, PEq) ]
via (PIsDataReprInstances PProposalLock) )
)
deriving stock
( -- | @since 0.1.0
Generic
)
deriving anyclass
( -- | @since 0.1.0
PlutusType
, -- | @since 0.1.0
PIsData
, -- | @since 0.1.0
PEq
)
instance DerivePlutusType PProposalLock where
type DPTStrat _ = PlutusTypeData
-- | @since 0.1.0
instance PTryFrom PData PProposalLock
-- | @since 0.2.0
instance PTryFrom PData (PAsData PProposalLock)
-- | @since 0.1.0
instance Plutarch.Lift.PUnsafeLiftDecl PProposalLock where
type PLifted PProposalLock = ProposalLock
-- | @since 0.1.0
deriving via deriving via
PAsData (PIsDataReprInstances PProposalLock) (DerivePConstantViaData ProposalLock PProposalLock)
instance instance
PTryFrom PData (PAsData PProposalLock) (Plutarch.Lift.PConstantDecl ProposalLock)
instance Plutarch.Lift.PUnsafeLiftDecl PProposalLock where type PLifted PProposalLock = ProposalLock -- | @since 0.2.0
deriving via (DerivePConstantViaData ProposalLock PProposalLock) instance (Plutarch.Lift.PConstantDecl ProposalLock) instance PShow PProposalLock where
pshow' :: Bool -> Term s PProposalLock -> Term s PString
pshow' True x = "(" <> pshow' False x <> ")"
pshow' False lock = pmatch lock $ \case
PCreated ((pfield @"created" #) -> pid) -> "PCreated " <> pshow' True pid
PVoted x -> pletFields @'["votedOn", "votedFor"] x $ \xF ->
"PVoted " <> pshow' True xF.votedOn <> " " <> pshow' True xF.votedFor
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
{- | Check whether a Stake is locked. If it is locked, various actions are unavailable. {- | Check whether a Stake is locked. If it is locked, various actions are unavailable.
@since 0.1.0 @since 0.2.0
-} -}
stakeLocked :: forall (s :: S). Term s (PStakeDatum :--> PBool) pstakeLocked :: forall (s :: S). Term s (PStakeDatum :--> PBool)
stakeLocked = phoistAcyclic $ pstakeLocked = phoistAcyclic $
plam $ \stakeDatum -> plam $ \stakeDatum ->
let locks :: Term _ (PBuiltinList (PAsData PProposalLock)) pnotNull #$ pfield @"lockedBy" @(PBuiltinList _) # pto stakeDatum
locks = pfield @"lockedBy" # stakeDatum
in pnotNull # locks
{- | Find a stake owned by a particular PK. {- | Get the number of *alive* proposals that were created by the given stake.
@since 0.1.0 @since 0.2.0
-} -}
findStakeOwnedBy :: pnumCreatedProposals :: Term s (PBuiltinList (PAsData PProposalLock) :--> PInteger)
Term pnumCreatedProposals =
s
( PAssetClass
:--> PPubKeyHash
:--> PBuiltinList (PAsData (PTuple PDatumHash PDatum))
:--> PBuiltinList (PAsData PTxInInfo)
:--> PMaybe (PAsData PStakeDatum)
)
findStakeOwnedBy = phoistAcyclic $
plam $ \ac pk datums inputs ->
pmatch (pfind # (isInputStakeOwnedBy # ac # pk # datums) # inputs) $ \case
PNothing -> pcon PNothing
PJust (pfromData -> v) -> unTermCont $ do
let txOut = pfield @"resolved" # pto v
txOutF <- tcont $ pletFields @'["datumHash"] $ txOut
pure $
pmatch txOutF.datumHash $ \case
PDNothing _ -> pcon PNothing
PDJust ((pfield @"_0" #) -> dh) ->
ptryFindDatum @(PAsData PStakeDatum) # dh # datums
{- | Check if a StakeDatum is owned by a particular public key.
@since 0.1.0
-}
stakeDatumOwnedBy :: Term _ (PPubKeyHash :--> PStakeDatum :--> PBool)
stakeDatumOwnedBy =
phoistAcyclic $ phoistAcyclic $
plam $ \pk stakeDatum -> plam $ \l ->
pletFields @'["owner"] (pto stakeDatum) $ \stakeDatumF -> pto $
stakeDatumF.owner #== pdata pk pfoldMap
# plam
{- | Does the input have a `Stake` owned by a particular PK? ( \(pfromData -> lock) -> pmatch lock $ \case
PCreated _ -> pcon $ PSum 1
@since 0.1.0 _ -> mempty
-}
isInputStakeOwnedBy ::
Term
_
( PAssetClass :--> PPubKeyHash
:--> PBuiltinList (PAsData (PTuple PDatumHash PDatum))
:--> PAsData PTxInInfo
:--> PBool
)
isInputStakeOwnedBy =
plam $ \ac ss datums txInInfo' -> unTermCont $ do
PTxInInfo ((pfield @"resolved" #) -> txOut) <- pmatchC $ pfromData txInInfo'
PTxOut txOut' <- pmatchC txOut
txOutF <- pletFieldsC @'["value", "datumHash"] txOut'
outStakeST <- pletC $ passetClassValueOf # txOutF.value # ac
pure $
pmatch txOutF.datumHash $ \case
PDNothing _ -> pcon PFalse
PDJust ((pfield @"_0" #) -> datumHash) ->
pif
(outStakeST #== 1)
( pmatch (ptryFindDatum @(PAsData PStakeDatum) # datumHash # datums) $ \case
PNothing -> pcon PFalse
PJust v -> stakeDatumOwnedBy # ss # pfromData (punsafeCoerce v)
) )
(pcon PFalse) # l
{- | Represent the usage of a stake on a particular proposal. {- | The role of a stake for a particular proposal. Scott-encoded.
A stake can be used to either create or vote on a proposal.
@since 0.1.0 @since 0.2.0
-} -}
data PStakeUsage (s :: S) data PStakeRole (s :: S)
= PVotedFor (Term s PResultTag) = -- | The stake was used to vote on the proposal.
| PCreated PVoter
| PDidNothing (Term s PResultTag)
-- ^ The option which was voted for.
| -- | The stake was used to create the proposal.
PCreator
| -- | The stake was used to both create and vote on the proposal.
PBoth
(Term s PResultTag)
-- ^ The option which was voted for.
| -- | The stake has nothing to do with the given proposal.
PIrrelevant
deriving stock deriving stock
( -- | @since 0.1.0 ( -- | @since 0.2.0
GHC.Generic Generic
) )
deriving anyclass deriving anyclass
( -- | @since 0.1.0 ( -- | @since 0.2.0
Generic
, -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.2.0
HasDatatypeInfo
, -- | @since 0.1.0
PEq PEq
) )
{- | / O(n) /.Return the usage of a stake on a particular proposal, instance DerivePlutusType PStakeRole where
given the 'lockedBy' field of a stake and the target proposal. type DPTStrat _ = PlutusTypeScott
@since 0.1.0 {- | Retutn true if the stake was used to voted on the proposal.
@since 0.2.0
-} -}
pgetStakeUsage :: pisVoter :: Term s (PStakeRole :--> PBool)
Term pisVoter = phoistAcyclic $
_ plam $ \sr -> pmatch sr $ \case
( PBuiltinList (PAsData PProposalLock) PVoter _ -> pconstant True
:--> PProposalId PBoth _ -> pconstant True
:--> PStakeUsage _ -> pconstant False
)
pgetStakeUsage = phoistAcyclic $
plam $ \locks pid ->
let -- All locks from the given proposal.
filteredLocks =
pmapMaybe
# plam
( \lock'@(pfromData -> lock) -> unTermCont $ do
lockF <- pletFieldsC @'["proposalTag"] lock
pure $ {- | Retutn true if the stake was used to create the proposal.
@since 0.2.0
-}
pisCreator :: Term s (PStakeRole :--> PBool)
pisCreator = phoistAcyclic $
plam $ \sr -> pmatch sr $ \case
PCreator -> pconstant True
PBoth _ -> pconstant True
_ -> pconstant False
{- | Retutn true if the stake was used to create the proposal, but not vote on
the proposal.
@since 0.2.0
-}
pisPureCreator :: Term s (PStakeRole :--> PBool)
pisPureCreator = phoistAcyclic $
plam $ \sr -> pmatch sr $ \case
PCreator -> pconstant True
_ -> pconstant False
{- | Return true if the stake isn't related to the proposal.
@since 0.2.0
-}
pisIrrelevant :: Term s (PStakeRole :--> PBool)
pisIrrelevant = phoistAcyclic $
plam $ \sr -> pmatch sr $ \case
PIrrelevant -> pconstant True
_ -> pconstant False
{- | Get the role of a stake for the proposal specified by the poroposal id,
given the 'StakeDatum.lockedBy' field of the stake.
Note that the list of locks is cosidered valid only if it contains at most
two locks from the given proposal: one voter lock and one creator lock.
@since 0.2.0
-}
pgetStakeRole :: Term s (PProposalId :--> PBuiltinList (PAsData PProposalLock) :--> PStakeRole)
pgetStakeRole = phoistAcyclic $
plam $ \pid locks ->
pfoldl
# plam
( \role (pfromData -> lock) ->
let thisRole = pmatch lock $ \case
PCreated ((pfield @"created" #) -> pid') ->
pif pif
(lockF.proposalTag #== pid) (pid' #== pid)
(pcon $ PJust lock') (pcon PCreator)
(pcon PNothing) (pcon PIrrelevant)
) PVoted lock' -> pletAll lock' $ \lockF ->
# locks pif
(lockF.votedOn #== pid)
(pcon $ PVoter lockF.votedFor)
(pcon PIrrelevant)
in pcombineStakeRole # thisRole # role
)
# pcon PIrrelevant
# locks
where
pcombineStakeRole :: Term s (PStakeRole :--> PStakeRole :--> PStakeRole)
pcombineStakeRole = phoistAcyclic $
plam $ \x y ->
let cannotCombine = ptraceError "duplicate roles"
in pmatch x $ \case
PVoter r -> pmatch y $ \case
PCreator -> pcon $ PBoth r
PIrrelevant -> x
_ -> cannotCombine
PCreator -> pmatch y $ \case
PVoter r -> pcon $ PBoth r
PIrrelevant -> x
_ -> cannotCombine
PBoth _ -> cannotCombine
PIrrelevant -> y
lockCount' = plength # filteredLocks {- | Get the outcome that was voted for.
in plet lockCount' $ \lockCount ->
pif (lockCount #== 0) (pcon PDidNothing) $ @since 0.2.0
pif -}
(lockCount #== 1) pextractVoteOption :: Term s (PStakeRole :--> PResultTag)
( pcon $ pextractVoteOption = phoistAcyclic $
PVotedFor $ plam $ \sr -> pmatch sr $ \case
pfromData $ PVoter r -> r
pfield @"vote" #$ phead # filteredLocks PBoth r -> r
) _ -> ptraceError "not voter"
-- Note: see the implementation of the governor.
(pcon PCreated)

View file

@ -8,36 +8,44 @@ Plutus Scripts for Stakes.
module Agora.Stake.Scripts (stakePolicy, stakeValidator) where module Agora.Stake.Scripts (stakePolicy, stakeValidator) where
import Agora.SafeMoney (GTTag) import Agora.SafeMoney (GTTag)
import Agora.Stake import Agora.Scripts (AgoraScripts, proposalSTAssetClass, stakeSTSymbol)
import Agora.Utils ( import Agora.Stake (
mustBePJust, PStakeDatum (PStakeDatum),
mustFindDatum', PStakeRedeemer (..),
pvalidatorHashToTokenName, StakeRedeemer (WitnessStake),
pstakeLocked,
) )
import Agora.Utils (
mustFindDatum',
)
import Data.Function (on)
import Data.Tagged (Tagged (..), untag) import Data.Tagged (Tagged (..), untag)
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
AmountGuarantees (Positive), AmountGuarantees (Positive),
PCredential (PPubKeyCredential, PScriptCredential), PCredential (PPubKeyCredential, PScriptCredential),
PDatumHash,
PMintingPolicy, PMintingPolicy,
PScriptPurpose (PMinting, PSpending), PScriptPurpose (PMinting, PSpending),
PTokenName, PTokenName,
PTxInfo, PTxInfo,
PTxOut,
PValidator, PValidator,
PValue, PValue,
mintingPolicySymbol,
mkMintingPolicy,
) )
import Plutarch.Api.V1.AssetClass (passetClass, passetClassValueOf, pvalueOf) import Plutarch.Api.V1.AssetClass (passetClass, passetClassValueOf, pvalueOf)
import Plutarch.Api.V1.ScriptContext (pfindTxInByTxOutRef, pisTokenSpent, ptxSignedBy, pvalueSpent) import Plutarch.Api.V1.ScriptContext (pfindTxInByTxOutRef, ptxSignedBy, pvalueSpent)
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (pgeqByClass', pgeqBySymbol, psymbolValueOf) import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (pgeqByClass', pgeqBySymbol, psymbolValueOf)
import Plutarch.Extra.Field (pletAllC)
import Plutarch.Extra.List (pmapMaybe, pmsortBy)
import Plutarch.Extra.Maybe (passertPJust, pdjust, pdnothing, pfromDJust, pmaybeData)
import Plutarch.Extra.Record (mkRecordConstr, (.&), (.=)) import Plutarch.Extra.Record (mkRecordConstr, (.&), (.=))
import Plutarch.Extra.TermCont (pguardC, pletC, pmatchC, ptryFromC) import Plutarch.Extra.TermCont (pguardC, pletC, pletFieldsC, pmatchC, ptryFromC)
import Plutarch.Internal (punsafeCoerce)
import Plutarch.Numeric.Additive (AdditiveMonoid (zero), AdditiveSemigroup ((+))) import Plutarch.Numeric.Additive (AdditiveMonoid (zero), AdditiveSemigroup ((+)))
import Plutarch.SafeMoney ( import Plutarch.SafeMoney (
pdiscreteValue', pdiscreteValue',
pvalueDiscrete', pvalueDiscrete',
) )
import Plutarch.Unsafe (punsafeCoerce)
import PlutusLedgerApi.V1.Value (AssetClass (AssetClass)) import PlutusLedgerApi.V1.Value (AssetClass (AssetClass))
import Prelude hiding (Num (..)) import Prelude hiding (Num (..))
@ -66,11 +74,11 @@ stakePolicy ::
ClosedTerm PMintingPolicy ClosedTerm PMintingPolicy
stakePolicy gtClassRef = stakePolicy gtClassRef =
plam $ \_redeemer ctx' -> unTermCont $ do plam $ \_redeemer ctx' -> unTermCont $ do
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx' ctx <- pletFieldsC @'["txInfo", "purpose"] ctx'
txInfo <- pletC $ ctx.txInfo txInfo <- pletC $ ctx.txInfo
let _a :: Term _ PTxInfo let _a :: Term _ PTxInfo
_a = txInfo _a = txInfo
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "signatories", "datums"] txInfo txInfoF <- pletFieldsC @'["mint", "inputs", "outputs", "signatories", "datums"] txInfo
PMinting ownSymbol' <- pmatchC $ pfromData ctx.purpose PMinting ownSymbol' <- pmatchC $ pfromData ctx.purpose
ownSymbol <- pletC $ pfield @"_0" # ownSymbol' ownSymbol <- pletC $ pfield @"_0" # ownSymbol'
@ -88,12 +96,16 @@ stakePolicy gtClassRef =
pany pany
# plam # plam
( \((pfield @"resolved" #) -> txOut) -> unTermCont $ do ( \((pfield @"resolved" #) -> txOut) -> unTermCont $ do
txOutF <- tcont $ pletFields @'["value", "datumHash"] txOut txOutF <- pletFieldsC @'["value", "datumHash"] txOut
pure $ pure $
pif pif
(psymbolValueOf # ownSymbol # txOutF.value #== 1) (psymbolValueOf # ownSymbol # txOutF.value #== 1)
( let datum = mustFindDatum' @PStakeDatum # txOutF.datumHash # txInfoF.datums ( let datum =
in pnot # (stakeLocked # datum) pfromData $
mustFindDatum' @(PAsData PStakeDatum)
# txOutF.datumHash
# txInfoF.datums
in pnot # (pstakeLocked # datum)
) )
(pconstant False) (pconstant False)
) )
@ -111,30 +123,30 @@ stakePolicy gtClassRef =
pguardC "A UTXO must exist with the correct output" $ pguardC "A UTXO must exist with the correct output" $
unTermCont $ do unTermCont $ do
let scriptOutputWithStakeST = let scriptOutputWithStakeST =
mustBePJust passertPJust
# "Output to script not found" # "Output to script not found"
#$ pfind #$ pfind
# plam # plam
( \output -> unTermCont $ do ( \output -> unTermCont $ do
outputF <- tcont $ pletFields @'["value", "address"] output outputF <- pletFieldsC @'["value", "address"] output
pure $ pure $
pmatch (pfromData $ pfield @"credential" # outputF.address) $ \case pmatch (pfromData $ pfield @"credential" # outputF.address) $ \case
-- Should pay to a script address -- Should pay to a script address
PPubKeyCredential _ -> pcon PFalse PPubKeyCredential _ -> pcon PFalse
PScriptCredential ((pfield @"_0" #) -> validatorHash) -> PScriptCredential ((pfield @"_0" #) -> validatorHash) ->
let tn :: Term _ PTokenName let tn :: Term _ PTokenName
tn = pvalidatorHashToTokenName validatorHash tn = punsafeCoerce $ pfromData validatorHash
in pvalueOf # outputF.value # ownSymbol # tn #== 1 in pvalueOf # outputF.value # ownSymbol # tn #== 1
) )
# pfromData txInfoF.outputs # pfromData txInfoF.outputs
outputF <- outputF <-
tcont $ pletFieldsC @'["value", "address", "datumHash"] scriptOutputWithStakeST
pletFields @'["value", "address", "datumHash"] scriptOutputWithStakeST
datumF <- datumF <-
tcont $ pletFieldsC @'["owner", "stakedAmount"] $
pletFields @'["owner", "stakedAmount"] $ pto $
mustFindDatum' @PStakeDatum # outputF.datumHash # txInfoF.datums pfromData $
mustFindDatum' @(PAsData PStakeDatum) # outputF.datumHash # txInfoF.datums
let hasExpectedStake = let hasExpectedStake =
ptraceIfFalse "Stake ouput has expected amount of stake token" $ ptraceIfFalse "Stake ouput has expected amount of stake token" $
@ -205,41 +217,58 @@ stakePolicy gtClassRef =
@since 0.1.0 @since 0.1.0
-} -}
stakeValidator :: Stake -> ClosedTerm PValidator stakeValidator ::
stakeValidator stake = -- | Lazy precompiled scripts.
AgoraScripts ->
-- | See 'Agora.Governor.Governor.gtClassRef'.
Tagged GTTag AssetClass ->
ClosedTerm PValidator
stakeValidator as gtClassRef =
plam $ \datum redeemer ctx' -> unTermCont $ do plam $ \datum redeemer ctx' -> unTermCont $ do
ctx <- tcont $ pletFields @'["txInfo", "purpose"] ctx' ctx <- pletFieldsC @'["txInfo", "purpose"] ctx'
txInfo <- pletC $ pfromData ctx.txInfo txInfo <- pletC $ pfromData ctx.txInfo
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "signatories", "datums"] txInfo txInfoF <-
pletFieldsC
@'[ "mint"
, "inputs"
, "outputs"
, "signatories"
, "datums"
]
txInfo
(pfromData -> stakeRedeemer, _) <- ptryFromC redeemer stakeRedeemer <- fst <$> ptryFromC redeemer
-- TODO: Use PTryFrom stakeDatum' <- pfromData . fst <$> ptryFromC datum
let stakeDatum' :: Term _ PStakeDatum stakeDatum <- pletAllC $ pto stakeDatum'
stakeDatum' = pfromData $ punsafeCoerce datum
stakeDatum <- tcont $ pletFields @'["owner", "stakedAmount", "lockedBy"] stakeDatum'
PSpending txOutRef <- pmatchC $ pfromData ctx.purpose PSpending txOutRef <- pmatchC $ pfromData ctx.purpose
PJust txInInfo <- pmatchC $ pfindTxInByTxOutRef # (pfield @"_0" # txOutRef) # txInfoF.inputs PJust ((pfield @"resolved" #) -> resolved) <-
ownAddress <- pletC $ pfield @"address" #$ pfield @"resolved" # txInInfo pmatchC $
let continuingValue :: Term _ (PValue _ _) pfindTxInByTxOutRef
continuingValue = pfield @"value" #$ pfield @"resolved" # txInInfo # (pfield @"_0" # txOutRef)
# txInfoF.inputs
resolvedF <- pletFieldsC @'["address", "value", "datumHash"] resolved
-- Whether the owner signs this transaction or not. -- Whether the owner signs this transaction or not.
ownerSignsTransaction <- pletC $ ptxSignedBy # txInfoF.signatories # stakeDatum.owner signedBy <- pletC $ ptxSignedBy # txInfoF.signatories
stCurrencySymbol <- pletC $ pconstant $ mintingPolicySymbol $ mkMintingPolicy (stakePolicy stake.gtClassRef) ownerSignsTransaction <- pletC $ signedBy # stakeDatum.owner
delegateSignsTransaction <-
pletC $
pmaybeData # pconstant False
# signedBy
# stakeDatum.delegatedTo
stCurrencySymbol <- pletC $ pconstant $ stakeSTSymbol as
mintedST <- pletC $ psymbolValueOf # stCurrencySymbol # txInfoF.mint mintedST <- pletC $ psymbolValueOf # stCurrencySymbol # txInfoF.mint
valueSpent <- pletC $ pvalueSpent # txInfoF.inputs valueSpent <- pletC $ pvalueSpent # txInfoF.inputs
spentST <- pletC $ psymbolValueOf # stCurrencySymbol #$ valueSpent spentST <- pletC $ psymbolValueOf # stCurrencySymbol #$ valueSpent
let AssetClass (propCs, propTn) = stake.proposalSTClass
proposalSTClass = passetClass # pconstant propCs # pconstant propTn
spentProposalST <- pletC $ passetClassValueOf # valueSpent # proposalSTClass
-- Is the stake currently locked? -- Is the stake currently locked?
stakeIsLocked <- pletC $ stakeLocked # stakeDatum' stakeIsLocked <- pletC $ pstakeLocked # stakeDatum'
pure $ pure $
pmatch stakeRedeemer $ \case pmatch stakeRedeemer $ \case
@ -255,196 +284,258 @@ stakeValidator stake =
pguardC "Owner signs this transaction" ownerSignsTransaction pguardC "Owner signs this transaction" ownerSignsTransaction
pure $ popaque (pconstant ()) pure $ popaque (pconstant ())
-------------------------------------------------------------------------- ------------------------------------------------------------------------
-- Handle redeemers that require own stake output. -- Handle redeemers that require own stake output.
_ -> unTermCont $ do _ -> unTermCont $ do
-- Filter out own output with own address and PST. let AssetClass (propCs, propTn) = proposalSTAssetClass as
ownOutput <- proposalSTClass = passetClass # pconstant propCs # pconstant propTn
spentProposalST = passetClassValueOf # valueSpent # proposalSTClass
proposalTokenMoved <- pletC $ 1 #<= spentProposalST
-- Filter out own outputs using own address and ST.
ownOutputs <-
pletC $ pletC $
mustBePJust # "Own output should be present" #$ pfind pfilter
# plam # plam
( \input -> unTermCont $ do ( \output -> unTermCont $ do
inputF <- tcont $ pletFields @'["address", "value"] input outputF <- pletFieldsC @'["address", "value"] output
pure $ pure $
inputF.address #== ownAddress outputF.address #== resolvedF.address
#&& psymbolValueOf # stCurrencySymbol # inputF.value #== 1 #&& psymbolValueOf # stCurrencySymbol # outputF.value #== 1
) )
# pfromData txInfoF.outputs # pfromData txInfoF.outputs
stakeOut <- let witnessStake = unTermCont $ do
pletC $ pguardC "Either owner signs the transaction or proposal token moved" $
mustFindDatum' @PStakeDatum ownerSignsTransaction #|| proposalTokenMoved
# (pfield @"datumHash" # ownOutput)
# txInfoF.datums
ownOutputValue <- -- FIXME: remove this once we have reference input.
pletC $ --
pfield @"value" # ownOutput -- Our goal here is to allow multiple input stakes, and also ensure that every the input stakes has a
-- corresponding output stake, which carries the same value and the same datum as the input stake.
ownOutputValueUnchanged <- --
pletC $ -- Validation strategy I have tried/considered so far:
pdata continuingValue #== pdata ownOutputValue -- 1. Check that the number of input stakes equals to the number of output stakes, and verify
-- that there's an output stake with the exact same value and datum hash as the stake being
stakeOutUnchanged <- -- validated , However this approach has a fatal vulnerability: let's say we have two totally
pletC $ -- identical stakes, a malicious user can comsume these two stakes and remove GTs from one of them.
pdata stakeOut #== pdata stakeDatum' -- 2. Perform the same checks as the last approch does, while also checking that every output stake is
-- valid(stakedAmount == actual value). However this requires that all the output stake datum are
pure $ -- included in the transaction, and we have to find and go through them one by one to access the
pmatch stakeRedeemer $ \case -- 'stakedAmount' fields, meaning that computationally this approach is *very* expensive.
PRetractVotes l -> unTermCont $ do -- 3. The one implemented below. Find all the continuous input/output, sort them by 'datumHash', and
pguardC -- ensure that the two sorted lists are equal.
"Owner signs this transaction" let ownInputs =
ownerSignsTransaction pmapMaybe
# plam
pguardC "ST at inputs must be 1" $ ( \input -> plet (pfield @"resolved" # input) $ \resolvedInput ->
spentST #== 1 let value = pfield @"value" # resolvedInput
in pif
-- This puts trust into the Proposal. The Proposal must necessarily check (psymbolValueOf # stCurrencySymbol # value #== 1)
-- that this is not abused. (pcon $ PJust resolvedInput)
pguardC "Proposal ST spent" $ (pcon PNothing)
spentProposalST #== 1
pguardC "A UTXO must exist with the correct output" $
let expectedLocks = pfield @"locks" # l
expectedDatum =
mkRecordConstr
PStakeDatum
( #stakedAmount .= stakeDatum.stakedAmount
.& #owner .= stakeDatum.owner
.& #lockedBy .= expectedLocks
) )
# pfromData txInfoF.inputs
valueCorrect = ownOutputValueUnchanged sortTxOuts :: Term _ (PBuiltinList PTxOut :--> PBuiltinList PTxOut)
outputDatumCorrect = stakeOut #== expectedDatum sortTxOuts = phoistAcyclic $ plam (pmsortBy # plam ((#<) `on` (getDatumHash #)) #)
in foldl1 where
(#&&) getDatumHash :: Term _ (PTxOut :--> PDatumHash)
[ ptraceIfFalse "valueCorrect" valueCorrect getDatumHash = phoistAcyclic $ plam ((pfromDJust #) . pfromData . (pfield @"datumHash" #))
, ptraceIfFalse "datumCorrect" outputDatumCorrect
]
pure $ popaque (pconstant ()) sortedOwnInputs = sortTxOuts # ownInputs
-------------------------------------------------------------------------- sortedOwnOutputs = sortTxOuts # ownOutputs
PPermitVote l -> unTermCont $ do
pguardC
"Owner signs this transaction"
ownerSignsTransaction
-- This puts trust into the Proposal. The Proposal must necessarily check pguardC "Every stake inputs has a corresponding unchanged output" $
-- that this is not abused. plistEquals # sortedOwnInputs # sortedOwnOutputs
pguardC "Proposal ST spent" $
spentProposalST #== 1
-- Update the stake datum, but only the 'lockedBy' field. pure $ popaque $ pconstant ()
let -- We actually don't know whether the given lock is valid or not. ----------------------------------------------------------------------
-- This is checked in the proposal validator.
newLock = pfield @"lock" # l
-- Prepend the new lock to the existing locks.
expectedLocks = pcons # newLock # stakeDatum.lockedBy
expectedDatum <- let onlyAcceptOneStake = unTermCont $ do
pguardC "ST at inputs must be 1" $
spentST #== 1
ownOutput <- pletC $ phead # ownOutputs
stakeOut <-
pletC $ pletC $
mkRecordConstr pfromData $
PStakeDatum mustFindDatum' @(PAsData PStakeDatum)
( #stakedAmount .= stakeDatum.stakedAmount # (pfield @"datumHash" # ownOutput)
.& #owner .= stakeDatum.owner # txInfoF.datums
.& #lockedBy .= pdata expectedLocks
)
pguardC "A UTXO must exist with the correct output" $ ownOutputValue <-
let correctOutputDatum = stakeOut #== expectedDatum pletC $
valueCorrect = ownOutputValueUnchanged pfield @"value" # ownOutput
in foldl1
(#&&)
[ ptraceIfFalse "valueCorrect" valueCorrect
, ptraceIfFalse "datumCorrect" correctOutputDatum
]
pure $ popaque (pconstant ()) ownOutputValueUnchanged <-
-------------------------------------------------------------------------- pletC $
PWitnessStake _ -> unTermCont $ do pdata resolvedF.value #== pdata ownOutputValue
pguardC "ST at inputs must be 1" $
spentST #== 1
let AssetClass (propCs, propTn) = stake.proposalSTClass onlyLocksUpdated <-
propAssetClass = passetClass # pconstant propCs # pconstant propTn pletC $
proposalTokenMoved = let templateStakeDatum =
pisTokenSpent
# propAssetClass
# txInfoF.inputs
-- In order for cosignature to be witnessed, it must be possible for a
-- proposal to allow this transaction to happen. This puts trust into the Proposal.
-- The Proposal must necessarily check that this is not abused.
pguardC
"Owner signs this transaction OR proposal token is spent"
(ownerSignsTransaction #|| proposalTokenMoved)
pguardC "A UTXO must exist with the correct output" $
let correctOutputDatum = stakeOutUnchanged
valueCorrect = ownOutputValueUnchanged
in foldl1
(#&&)
[ ptraceIfFalse "valueCorrect" valueCorrect
, ptraceIfFalse "correctOutputDatum" correctOutputDatum
]
pure $ popaque (pconstant ())
--------------------------------------------------------------------------
PDepositWithdraw r -> unTermCont $ do
pguardC "ST at inputs must be 1" $
spentST #== 1
pguardC "Stake unlocked" $
pnot #$ stakeIsLocked
pguardC
"Owner signs this transaction"
ownerSignsTransaction
pguardC "A UTXO must exist with the correct output" $
unTermCont $ do
let oldStakedAmount = pfromData $ stakeDatum.stakedAmount
delta = pfromData $ pfield @"delta" # r
newStakedAmount <- pletC $ oldStakedAmount + delta
pguardC "New staked amount shoudl be greater than or equal to 0" $
zero #<= newStakedAmount
let expectedDatum =
mkRecordConstr mkRecordConstr
PStakeDatum PStakeDatum
( #stakedAmount .= pdata newStakedAmount ( #stakedAmount .= stakeDatum.stakedAmount
.& #owner .= stakeDatum.owner .& #owner .= stakeDatum.owner
.& #lockedBy .= stakeDatum.lockedBy .& #delegatedTo .= stakeDatum.delegatedTo
.& #lockedBy .= pfield @"lockedBy" # pto stakeOut
) )
datumCorrect = stakeOut #== expectedDatum in stakeOut #== templateStakeDatum
let valueDelta :: Term _ (PValue _ 'Positive) setDelegate <- pletC $
valueDelta = pdiscreteValue' stake.gtClassRef # delta plam $ \maybePkh -> unTermCont $ do
pguardC
"Owner signs this transaction"
ownerSignsTransaction
expectedValue = pguardC "A UTXO must exist with the correct output" $
continuingValue <> valueDelta let correctOutputDatum =
stakeOut
valueCorrect = #== mkRecordConstr
foldr1 PStakeDatum
( #stakedAmount .= stakeDatum.stakedAmount
.& #owner .= stakeDatum.owner
.& #delegatedTo .= pdata maybePkh
.& #lockedBy .= stakeDatum.lockedBy
)
valueCorrect = ownOutputValueUnchanged
in foldl1
(#&&) (#&&)
[ pgeqByClass' (AssetClass ("", "")) [ ptraceIfFalse "valueCorrect" valueCorrect
# ownOutputValue , ptraceIfFalse "datumCorrect" correctOutputDatum
# expectedValue
, pgeqByClass' (untag stake.gtClassRef)
# ownOutputValue
# expectedValue
, pgeqBySymbol
# stCurrencySymbol
# ownOutputValue
# expectedValue
] ]
--
pure $ pure $ popaque (pconstant ())
foldl1
(#&&) pure $
[ ptraceIfFalse "valueCorrect" valueCorrect pmatch stakeRedeemer $ \case
, ptraceIfFalse "datumCorrect" datumCorrect PRetractVotes _ -> unTermCont $ do
] pguardC
-- "Owner or delegate signs this transaction"
pure $ popaque (pconstant ()) $ ownerSignsTransaction #|| delegateSignsTransaction
_ -> popaque (pconstant ())
-- This puts trust into the Proposal. The Proposal must necessarily check
-- that this is not abused.
pguardC "Proposal ST spent" proposalTokenMoved
pguardC "A UTXO must exist with the correct output" $
let valueCorrect = ownOutputValueUnchanged
outputDatumCorrect = onlyLocksUpdated
in foldl1
(#&&)
[ ptraceIfFalse "valueCorrect" valueCorrect
, ptraceIfFalse "datumCorrect" outputDatumCorrect
]
pure $ popaque (pconstant ())
------------------------------------------------------------
PPermitVote _ -> unTermCont $ do
pguardC
"Owner or delegate signs this transaction"
$ ownerSignsTransaction #|| delegateSignsTransaction
let proposalTokenMinted =
passetClassValueOf # txInfoF.mint # proposalSTClass #== 1
-- This puts trust into the Proposal. The Proposal must necessarily check
-- that this is not abused.
pguardC "Proposal ST spent or minted" $
proposalTokenMoved #|| proposalTokenMinted
pguardC "A UTXO must exist with the correct output" $
let correctOutputDatum = onlyLocksUpdated
valueCorrect = ownOutputValueUnchanged
in foldl1
(#&&)
[ ptraceIfFalse "valueCorrect" valueCorrect
, ptraceIfFalse "datumCorrect" correctOutputDatum
]
pure $ popaque (pconstant ())
------------------------------------------------------------
PDepositWithdraw r -> unTermCont $ do
pguardC "Stake unlocked" $
pnot #$ stakeIsLocked
pguardC
"Owner signs this transaction"
ownerSignsTransaction
pguardC "A UTXO must exist with the correct output" $
unTermCont $ do
let oldStakedAmount = pfromData $ stakeDatum.stakedAmount
delta = pfromData $ pfield @"delta" # r
newStakedAmount <- pletC $ oldStakedAmount + delta
pguardC "New staked amount should be greater than or equal to 0" $
zero #<= newStakedAmount
let expectedDatum =
mkRecordConstr
PStakeDatum
( #stakedAmount .= pdata newStakedAmount
.& #owner .= stakeDatum.owner
.& #delegatedTo .= stakeDatum.delegatedTo
.& #lockedBy .= stakeDatum.lockedBy
)
datumCorrect = stakeOut #== expectedDatum
let valueDelta :: Term _ (PValue _ 'Positive)
valueDelta = pdiscreteValue' gtClassRef # delta
expectedValue =
resolvedF.value <> valueDelta
valueCorrect =
foldr1
(#&&)
[ pgeqByClass' (AssetClass ("", ""))
# ownOutputValue
# expectedValue
, pgeqByClass' (untag gtClassRef)
# ownOutputValue
# expectedValue
, pgeqBySymbol
# stCurrencySymbol
# ownOutputValue
# expectedValue
]
--
pure $
foldl1
(#&&)
[ ptraceIfFalse "valueCorrect" valueCorrect
, ptraceIfFalse "datumCorrect" datumCorrect
]
--
pure $ popaque (pconstant ())
------------------------------------------------------------
PDelegateTo ((pfromData . (pfield @"pkh" #)) -> pkh) -> unTermCont $ do
pguardC "Cannot delegate to the owner" $
pnot #$ stakeDatum.owner #== pkh
pure $ setDelegate #$ pdjust # pdata pkh
------------------------------------------------------------
PClearDelegate _ ->
setDelegate # pdnothing
------------------------------------------------------------
_ -> popaque (pconstant ())
pure $
pif
(pdata stakeRedeemer #== pconstantData WitnessStake)
witnessStake
onlyAcceptOneStake

View file

@ -11,16 +11,17 @@ treasury.
module Agora.Treasury (module Agora.Treasury) where module Agora.Treasury (module Agora.Treasury) where
import Agora.AuthorityToken (singleAuthorityTokenBurned) import Agora.AuthorityToken (singleAuthorityTokenBurned)
import GHC.Generics qualified as GHC import Generics.SOP qualified as SOP
import Generics.SOP (Generic, I (I))
import Plutarch.Api.V1 (PValidator) import Plutarch.Api.V1 (PValidator)
import Plutarch.Api.V1.Contexts (PScriptPurpose (PMinting)) import Plutarch.Api.V1.Contexts (PScriptPurpose (PMinting))
import "plutarch" Plutarch.Api.V1.Value (PValue) import "plutarch" Plutarch.Api.V1.Value (PValue)
import Plutarch.DataRepr ( import Plutarch.Builtin (pforgetData)
DerivePConstantViaData (..), import Plutarch.Extra.IsData (
PIsDataReprInstances (PIsDataReprInstances), DerivePConstantViaEnum (..),
EnumIsData (..),
PlutusTypeEnumData,
) )
import Plutarch.Extra.TermCont (pguardC, pletC, pmatchC, ptryFromC) import Plutarch.Extra.TermCont (pguardC, pletC, pletFieldsC, pmatchC)
import Plutarch.Lift (PConstantDecl (..), PLifted (..), PUnsafeLiftDecl) import Plutarch.Lift (PConstantDecl (..), PLifted (..), PUnsafeLiftDecl)
import Plutarch.TryFrom () import Plutarch.TryFrom ()
import PlutusLedgerApi.V1.Value (CurrencySymbol) import PlutusLedgerApi.V1.Value (CurrencySymbol)
@ -39,14 +40,23 @@ data TreasuryRedeemer
, -- | @since 0.1.0 , -- | @since 0.1.0
Show Show
, -- | @since 0.1.0 , -- | @since 0.1.0
GHC.Generic Generic
, -- | @since 0.2.0
Enum
, -- | @since 0.2.0
Bounded
) )
deriving anyclass
-- | @since 0.1.0 ( -- | @since 0.2.0
PlutusTx.makeIsDataIndexed SOP.Generic
''TreasuryRedeemer )
[ ('SpendTreasuryGAT, 0) deriving
] ( -- | @since 0.1.0
PlutusTx.ToData
, -- | @since 0.1.0
PlutusTx.FromData
)
via (EnumIsData TreasuryRedeemer)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -55,33 +65,25 @@ PlutusTx.makeIsDataIndexed
@since 0.1.0 @since 0.1.0
-} -}
newtype PTreasuryRedeemer (s :: S) data PTreasuryRedeemer (s :: S)
= -- | Alters treasury parameters, subject to the burning of a = PSpendTreasuryGAT
-- governance authority token.
PSpendTreasuryGAT (Term s (PDataRecord '[]))
deriving stock deriving stock
( -- | @since 0.1.0 ( -- | @since 0.1.0
GHC.Generic Generic
, -- | @since 0.2.0
Bounded
, -- | @since 0.2.0
Enum
) )
deriving anyclass deriving anyclass
( -- | @since 0.1.0
Generic
, -- | @since 0.1.0
PIsDataRepr
)
deriving
( -- | @since 0.1.0 ( -- | @since 0.1.0
PlutusType PlutusType
, -- | @since 0.1.0 , -- | @since 0.1.0
PIsData PIsData
) )
via PIsDataReprInstances PTreasuryRedeemer
-- | @since 0.1.0 instance DerivePlutusType PTreasuryRedeemer where
deriving via type DPTStrat _ = PlutusTypeEnumData
PAsData (PIsDataReprInstances PTreasuryRedeemer)
instance
PTryFrom PData (PAsData PTreasuryRedeemer)
-- | @since 0.1.0 -- | @since 0.1.0
instance PUnsafeLiftDecl PTreasuryRedeemer where instance PUnsafeLiftDecl PTreasuryRedeemer where
@ -89,7 +91,7 @@ instance PUnsafeLiftDecl PTreasuryRedeemer where
-- | @since 0.1.0 -- | @since 0.1.0
deriving via deriving via
(DerivePConstantViaData TreasuryRedeemer PTreasuryRedeemer) (DerivePConstantViaEnum TreasuryRedeemer PTreasuryRedeemer)
instance instance
(PConstantDecl TreasuryRedeemer) (PConstantDecl TreasuryRedeemer)
@ -105,26 +107,24 @@ treasuryValidator ::
CurrencySymbol -> CurrencySymbol ->
ClosedTerm PValidator ClosedTerm PValidator
treasuryValidator gatCs' = plam $ \_datum redeemer ctx' -> unTermCont $ do treasuryValidator gatCs' = plam $ \_datum redeemer ctx' -> unTermCont $ do
(treasuryRedeemer, _) <- ptryFromC redeemer
-- plet required fields from script context. -- plet required fields from script context.
ctx <- tcont $ pletFields @["txInfo", "purpose"] ctx' ctx <- pletFieldsC @["txInfo", "purpose"] ctx'
-- Ensure that script is for burning i.e. minting a negative amount. -- Ensure that script is for burning i.e. minting a negative amount.
PMinting _ <- pmatchC ctx.purpose PMinting _ <- pmatchC ctx.purpose
-- Ensure redeemer type is valid. -- Ensure redeemer type is valid.
PSpendTreasuryGAT _ <- pmatchC $ pfromData treasuryRedeemer pguardC "Redeemer should be SpendTreasuryGAT" $
redeemer #== pforgetData (pconstantData SpendTreasuryGAT)
-- Get the minted value from txInfo. -- Get the minted value from txInfo.
txInfo' <- pletC ctx.txInfo txInfo <- pletFieldsC @'["mint", "inputs"] ctx.txInfo
txInfo <- tcont $ pletFields @'["mint"] txInfo'
let mint :: Term _ (PValue _ _) let mint :: Term _ (PValue _ _)
mint = txInfo.mint mint = txInfo.mint
gatCs <- pletC $ pconstant gatCs' gatCs <- pletC $ pconstant gatCs'
pguardC "A single authority token has been burned" $ pguardC "A single authority token has been burned" $
singleAuthorityTokenBurned gatCs txInfo' mint singleAuthorityTokenBurned gatCs txInfo.inputs mint
pure . popaque $ pconstant () pure . popaque $ pconstant ()

View file

@ -1,3 +1,6 @@
{-# LANGUAGE QuantifiedConstraints #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{- | {- |
Module : Agora.Utils Module : Agora.Utils
Maintainer : emi@haskell.fyi Maintainer : emi@haskell.fyi
@ -6,49 +9,32 @@ Description: Plutarch utility functions that should be upstreamed or don't belon
Plutarch utility functions that should be upstreamed or don't belong anywhere else. Plutarch utility functions that should be upstreamed or don't belong anywhere else.
-} -}
module Agora.Utils ( module Agora.Utils (
scriptHashFromAddress,
findOutputsToAddress,
findTxOutDatum,
validatorHashToTokenName, validatorHashToTokenName,
pvalidatorHashToTokenName,
getMintingPolicySymbol,
hasOnlyOneTokenOfCurrencySymbol,
mustFindDatum', mustFindDatum',
mustBePJust,
mustBePDJust,
validatorHashToAddress, validatorHashToAddress,
isScriptAddress, pltAsData,
isPubKey, withBuiltinPairAsData,
CompiledValidator (..),
CompiledMintingPolicy (..),
CompiledEffect (..),
) where ) where
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
AmountGuarantees,
KeyGuarantees,
PAddress,
PCredential (PScriptCredential),
PCurrencySymbol,
PDatum, PDatum,
PDatumHash, PDatumHash,
PMaybeData (PDJust), PMaybeData,
PMintingPolicy,
PTokenName (PTokenName),
PTuple, PTuple,
PTxOut,
PValidatorHash,
PValue,
mintingPolicySymbol,
mkMintingPolicy,
) )
import Plutarch.Api.V1.ScriptContext (pfindDatum)
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (psymbolValueOf)
import Plutarch.Builtin (pforgetData) import Plutarch.Builtin (pforgetData)
import Plutarch.Extra.List (plookupTuple) import Plutarch.Extra.List (plookupTuple)
import Plutarch.Extra.TermCont (pletC, pmatchC) import Plutarch.Extra.Maybe (passertPDJust, passertPJust)
import Plutarch.Extra.TermCont (ptryFromC)
import PlutusLedgerApi.V1 ( import PlutusLedgerApi.V1 (
Address (..), Address (..),
Credential (..), Credential (..),
CurrencySymbol, MintingPolicy,
TokenName (..), TokenName (..),
Validator,
ValidatorHash (..), ValidatorHash (..),
) )
@ -56,60 +42,6 @@ import PlutusLedgerApi.V1 (
All of these functions are quite inefficient. All of these functions are quite inefficient.
-} -}
{- | Get script hash from an Address.
@since 0.1.0
-}
scriptHashFromAddress :: Term s (PAddress :--> PMaybe PValidatorHash)
scriptHashFromAddress = phoistAcyclic $
plam $ \addr ->
pmatch (pfromData $ pfield @"credential" # addr) $ \case
PScriptCredential ((pfield @"_0" #) -> h) -> pcon $ PJust h
_ -> pcon PNothing
{- | Return true if the given address is a script address.
@since 0.1.0
-}
isScriptAddress :: Term s (PAddress :--> PBool)
isScriptAddress = phoistAcyclic $
plam $ \addr -> pnot #$ isPubKey #$ pfromData $ pfield @"credential" # addr
{- | Return true if the given credential is a pub-key-hash.
@since 0.1.0
-}
isPubKey :: Term s (PCredential :--> PBool)
isPubKey = phoistAcyclic $
plam $ \cred ->
pmatch cred $ \case
PScriptCredential _ -> pconstant False
_ -> pconstant True
{- | Find all TxOuts sent to an Address
@since 0.1.0
-}
findOutputsToAddress :: Term s (PBuiltinList (PAsData PTxOut) :--> PAddress :--> PBuiltinList (PAsData PTxOut))
findOutputsToAddress = phoistAcyclic $
plam $ \outputs address' -> unTermCont $ do
address <- pletC $ pdata address'
pure $
pfilter # plam (\(pfromData -> txOut) -> pfield @"address" # txOut #== address)
# outputs
{- | Find the data corresponding to a TxOut, if there is one
@since 0.1.0
-}
findTxOutDatum :: Term s (PBuiltinList (PAsData (PTuple PDatumHash PDatum)) :--> PTxOut :--> PMaybe PDatum)
findTxOutDatum = phoistAcyclic $
plam $ \datums out -> unTermCont $ do
datumHash' <- pmatchC $ pfromData $ pfield @"datumHash" # out
pure $ case datumHash' of
PDJust ((pfield @"_0" #) -> datumHash) -> pfindDatum # datumHash # datums
_ -> pcon PNothing
{- | Safely convert a 'PValidatorHash' into a 'PTokenName'. This can be useful for tagging {- | Safely convert a 'PValidatorHash' into a 'PTokenName'. This can be useful for tagging
tokens for extra safety. tokens for extra safety.
@ -118,39 +50,13 @@ findTxOutDatum = phoistAcyclic $
validatorHashToTokenName :: ValidatorHash -> TokenName validatorHashToTokenName :: ValidatorHash -> TokenName
validatorHashToTokenName (ValidatorHash hash) = TokenName hash validatorHashToTokenName (ValidatorHash hash) = TokenName hash
{- | Plutarch level 'validatorHashToTokenName'.
@since 0.1.0
-}
pvalidatorHashToTokenName :: forall (s :: S). Term s PValidatorHash -> Term s PTokenName
pvalidatorHashToTokenName vh = pcon (PTokenName (pto vh))
{- | Get the CurrencySymbol of a PMintingPolicy.
@since 0.1.0
-}
getMintingPolicySymbol :: ClosedTerm PMintingPolicy -> CurrencySymbol
getMintingPolicySymbol v = mintingPolicySymbol $ mkMintingPolicy v
{- | The entire value only contains one token of the given currency symbol.
@since 0.1.0
-}
hasOnlyOneTokenOfCurrencySymbol ::
forall (keys :: KeyGuarantees) (amounts :: AmountGuarantees) (s :: S).
Term s (PCurrencySymbol :--> PValue keys amounts :--> PBool)
hasOnlyOneTokenOfCurrencySymbol = phoistAcyclic $
plam $ \cs vs -> P.do
psymbolValueOf # cs # vs #== 1
#&& (plength #$ pto $ pto $ pto vs) #== 1
{- | Find datum given a maybe datum hash {- | Find datum given a maybe datum hash
@since 0.1.0 @since 0.1.0
-} -}
mustFindDatum' :: mustFindDatum' ::
forall (datum :: PType). forall (datum :: PType).
(PIsData datum, PTryFrom PData (PAsData datum)) => (PIsData datum, PTryFrom PData datum) =>
forall s. forall s.
Term Term
s s
@ -160,32 +66,10 @@ mustFindDatum' ::
) )
mustFindDatum' = phoistAcyclic $ mustFindDatum' = phoistAcyclic $
plam $ \mdh datums -> unTermCont $ do plam $ \mdh datums -> unTermCont $ do
let dh = mustBePDJust # "Given TxOut dones't have a datum" # mdh let dh = passertPDJust # "Given TxOut dones't have a datum" # mdh
dt = mustBePJust # "Datum not found in the transaction" #$ plookupTuple # dh # datums dt = passertPJust # "Datum not found in the transaction" #$ plookupTuple # dh # datums
(d, _) <- tcont $ ptryFrom $ pforgetData $ pdata dt (d, _) <- ptryFromC $ pforgetData $ pdata dt
pure $ pfromData d pure d
{- | Extract the value stored in a PMaybe container.
If there's no value, throw an error with the given message.
@since 0.1.0
-}
mustBePJust :: forall a s. Term s (PString :--> PMaybe a :--> a)
mustBePJust = phoistAcyclic $
plam $ \emsg mv' -> pmatch mv' $ \case
PJust v -> v
_ -> ptraceError emsg
{- | Extract the value stored in a PMaybeData container.
If there's no value, throw an error with the given message.
@since 0.1.0
-}
mustBePDJust :: forall a s. (PIsData a) => Term s (PString :--> PMaybeData a :--> a)
mustBePDJust = phoistAcyclic $
plam $ \emsg mv' -> pmatch mv' $ \case
PDJust ((pfield @"_0" #) -> v) -> v
_ -> ptraceError emsg
{- | Create an 'Address' from a given 'ValidatorHash' with no 'PlutusLedgerApi.V1.Credential.StakingCredential'. {- | Create an 'Address' from a given 'ValidatorHash' with no 'PlutusLedgerApi.V1.Credential.StakingCredential'.
@ -193,3 +77,56 @@ mustBePDJust = phoistAcyclic $
-} -}
validatorHashToAddress :: ValidatorHash -> Address validatorHashToAddress :: ValidatorHash -> Address
validatorHashToAddress vh = Address (ScriptCredential vh) Nothing validatorHashToAddress vh = Address (ScriptCredential vh) Nothing
{- | Compare two 'PAsData' value, return true if the first one is less than the second one.
@since 0.2.0
-}
pltAsData ::
forall (a :: PType) (s :: S).
(POrd a, PIsData a) =>
Term s (PAsData a :--> PAsData a :--> PBool)
pltAsData = phoistAcyclic $
plam $
\(pfromData -> l) (pfromData -> r) -> l #< r
{- | Extract data stored in a 'PBuiltinPair' and call a function to process it.
@since 0.2.0
-}
withBuiltinPairAsData ::
forall (a :: PType) (b :: PType) (c :: PType) (s :: S).
(PIsData a, PIsData b) =>
(Term s a -> Term s b -> Term s c) ->
Term
s
(PBuiltinPair (PAsData a) (PAsData b)) ->
Term s c
withBuiltinPairAsData f p =
let a = pfromData $ pfstBuiltin # p
b = pfromData $ psndBuiltin # p
in f a b
{- | Type-safe wrapper for compiled plutus validator.
@since 0.2.0
-}
newtype CompiledValidator (datum :: Type) (redeemer :: Type) = CompiledValidator
{ getCompiledValidator :: Validator
}
{- | Type-safe wrapper for compiled plutus miting policy.
@since 0.2.0
-}
newtype CompiledMintingPolicy (redeemer :: Type) = CompiledMintingPolicy
{ getCompiledMintingPolicy :: MintingPolicy
}
{- | Type-safe wrapper for compiled plutus effect.
@since 0.2.0
-}
newtype CompiledEffect (datum :: Type) = CompiledEffect
{ getCompiledEffect :: Validator
}

617
bench.csv
View file

@ -1,37 +1,582 @@
name,cpu,mem,size name,cpu,mem,size
Agora/Effects/Treasury Withdrawal Effect/effect/Simple,333327612,830203,3674 Agora/Effects/Treasury Withdrawal Effect/effect/Simple,380214695,980182,4275
Agora/Effects/Treasury Withdrawal Effect/effect/Simple with multiple treasuries ,492387542,1197315,3986 Agora/Effects/Treasury Withdrawal Effect/effect/Simple with multiple treasuries ,544143721,1366494,4691
Agora/Effects/Treasury Withdrawal Effect/effect/Mixed Assets,456007605,1104500,3859 Agora/Effects/Treasury Withdrawal Effect/effect/Mixed Assets,545045362,1387355,4636
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/governor validator should pass,90397270,249528,8807 Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/governor validator should pass,112721874,312363,9413
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/effect validator should pass,106082031,292993,3609 Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/effect validator should pass,152563857,422397,4692
Agora/Stake/policy/stakeCreation,52241265,152127,2514 Agora/Stake/policy/stakeCreation,54323406,159125,2646
Agora/Stake/validator/stakeDepositWithdraw deposit,180880812,492023,4431 Agora/Stake/validator/stakeDepositWithdraw deposit,194804304,538628,5315
Agora/Stake/validator/stakeDepositWithdraw withdraw,180880812,492023,4419 Agora/Stake/validator/stakeDepositWithdraw withdraw,194804304,538628,5303
Agora/Proposal/policy/proposalCreation,23140177,69194,1519 Agora/Stake/validator/set delegate/override existing delegate,117949415,309090,5367
Agora/Proposal/validator/cosignature/proposal,338483402,961112,8620 Agora/Stake/validator/set delegate/remove existing delegate,115539973,301757,5304
Agora/Proposal/validator/cosignature/stake,126327509,315061,4968 Agora/Stake/validator/set delegate/set delegate to something,114218077,301022,5304
Agora/Proposal/validator/voting/proposal,296656410,830692,8549 Agora/Proposal/policy (proposal creation)/legal/proposal,33965644,101486,1971
Agora/Proposal/validator/voting/stake,121170376,320853,4942 Agora/Proposal/policy (proposal creation)/legal/governor,369498544,984529,9918
Agora/Proposal/validator/advancing/successfully advance to next state/Draft -> VotringReady,294340341,825452,8447 Agora/Proposal/policy (proposal creation)/legal/stake,168978875,446628,5969
Agora/Proposal/validator/advancing/successfully advance to next state/VotingReady -> Locked,306801371,861382,8456 Agora/Proposal/policy (proposal creation)/illegal/invalid next proposal id/proposal,33965644,101486,1971
Agora/Proposal/validator/advancing/successfully advance to next state/Locked -> Finished,295193386,827555,8456 Agora/Proposal/policy (proposal creation)/illegal/invalid next proposal id/stake,168978875,446628,5969
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Draft -> Finished,293210540,822722,8449 Agora/Proposal/policy (proposal creation)/illegal/use other's stake/proposal,33965644,101486,1940
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/VotingReady -> Finished,291801629,820017,8450 Agora/Proposal/policy (proposal creation)/illegal/use other's stake/governor,369498544,984529,9887
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Locked -> Finished,292932607,822421,8450 Agora/Proposal/policy (proposal creation)/illegal/altered stake/proposal,33965644,101486,1971
"Agora/Proposal/validator/unlocking/legal/1 proposals, voter, unlock stake + retract votes, VotingReady",302502183,848154,8500 Agora/Proposal/policy (proposal creation)/illegal/invalid stake locks/proposal,33965644,101486,1979
"Agora/Proposal/validator/unlocking/legal/1 proposals, creator, unlock stake, Finished",273224492,773388,8504 Agora/Proposal/policy (proposal creation)/illegal/invalid stake locks/stake,174412535,461278,5977
"Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/1 proposals, voter, unlock stake, Finished",268700821,763033,8504 Agora/Proposal/policy (proposal creation)/illegal/has reached maximum proposals limit/proposal,33965644,101486,1991
"Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/1 proposals, voter, unlock stake, Locked",268700821,763033,8504 Agora/Proposal/policy (proposal creation)/illegal/has reached maximum proposals limit/stake,181677311,482844,5999
"Agora/Proposal/validator/unlocking/legal/42 proposals, voter, unlock stake + retract votes, VotingReady",2908014422,8180225,30018 Agora/Proposal/policy (proposal creation)/illegal/loose time range/proposal,33965644,101486,1971
"Agora/Proposal/validator/unlocking/legal/42 proposals, creator, unlock stake, Finished",2616129517,7383326,30287 Agora/Proposal/policy (proposal creation)/illegal/loose time range/stake,168978875,446628,5969
"Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/42 proposals, voter, unlock stake, Finished",2464384686,6936321,30187 Agora/Proposal/policy (proposal creation)/illegal/open time range/proposal,33965644,101486,1967
"Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/42 proposals, voter, unlock stake, Locked",2464384686,6936321,30187 Agora/Proposal/policy (proposal creation)/illegal/open time range/stake,168978875,446628,5965
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806 Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/VotingReady/proposal,33965644,101486,1971
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900 Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/VotingReady/stake,168978875,446628,5969
Agora/Treasury/Validator/Positive/Allows for effect changes,29938856,79744,1391 Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Locked/proposal,33965644,101486,1971
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806 Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Locked/stake,168978875,446628,5969
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900 Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Finished/proposal,33965644,101486,1971
Agora/Governor/policy/GST minting,51007235,144191,2034 Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Finished/stake,168978875,446628,5969
Agora/Governor/validator/proposal creation,317651809,854963,9323 Agora/Proposal/validator/cosignature/legal/with 1 cosigners/proposal,278329834,780402,9534
Agora/Governor/validator/GATs minting,423756405,1151000,9444 Agora/Proposal/validator/cosignature/legal/with 1 cosigners/stake,132939473,344002,5780
Agora/Governor/validator/mutate governor state,91544121,254987,8908 Agora/Proposal/validator/cosignature/legal/with 5 cosigners/proposal,733369909,2047977,12188
Agora/Proposal/validator/cosignature/legal/with 5 cosigners/stake,584246489,1537942,8314
Agora/Proposal/validator/cosignature/legal/with 10 cosigners/proposal,1453961927,4008580,15507
Agora/Proposal/validator/cosignature/legal/with 10 cosigners/stake,1244034344,3305773,11482
Agora/Proposal/validator/cosignature/illegal/duplicate cosigners/stake,132939473,344002,5780
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: VotingReady/stake,132939473,344002,5780
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: Locked/stake,132939473,344002,5780
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: Finished/stake,132939473,344002,5780
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: VotingReady/stake,584246489,1537942,8314
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: Locked/stake,584246489,1537942,8314
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: Finished/stake,584246489,1537942,8314
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: VotingReady/stake,1244034344,3305773,11482
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: Locked/stake,1244034344,3305773,11482
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: Finished/stake,1244034344,3305773,11482
Agora/Proposal/validator/voting/legal/ordinary/proposal,300100170,843476,9516
Agora/Proposal/validator/voting/legal/ordinary/stake,157219895,416300,5803
Agora/Proposal/validator/voting/legal/delegate/proposal,301216160,847088,9579
Agora/Proposal/validator/voting/legal/delegate/stake,161458884,426174,5897
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from Draft to VotingReady/proposal,293553258,818566,10029
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from Draft to VotingReady/stake,144038574,373470,6116
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from VotingReady to Locked/proposal,339788223,934150,10038
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from VotingReady to Locked/stake,144761110,375734,6123
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from Locked to Finished/proposal,389689216,1074147,11190
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from Locked to Finished/stake,201040711,510637,7275
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from Locked to Finished/governor,494243991,1258194,11233
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from Locked to Finished/authority,15875303,48540,3625
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to failed state/from Draft to Finished/proposal,290760522,812653,10031
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to failed state/from Draft to Finished/stake,144761110,375734,6118
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to failed state/from VotingReady to Finished/proposal,327404759,899415,10032
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to failed state/from VotingReady to Finished/stake,144761110,375734,6119
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to failed state/from Locked to Finished/proposal,329666715,904223,10032
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to failed state/from Locked to Finished/stake,144761110,375734,6119
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake,144038574,373470,6116
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake,144761110,375734,6123
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake,201040711,510637,7275
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/authority,15875303,48540,3625
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/insufficient cosigns/stake,144038574,373470,6116
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/insufficient votes/stake,144761110,375734,6119
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/ambiguous winning effect/stake,144761110,375734,6127
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/to next state too late/from Draft/stake,144038574,373470,6118
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/to next state too late/from VotingReady/stake,144761110,375734,6123
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/to next state too late/from Locked/stake,201040711,510637,7275
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/to next state too late/from Locked/governor,494243991,1258194,11233
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/to next state too late/from Locked/authority,15875303,48540,3625
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/altered output stake datum/from Locked/governor,494243991,1258194,11234
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/altered output stake datum/from Locked/authority,15875303,48540,3626
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/forget to mint GATs/proposal,365438170,1001538,10479
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/forget to mint GATs/stake,187940552,485026,6564
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/mint GATs for wrong validators/proposal,397772898,1098350,11417
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/mint GATs for wrong validators/stake,204588820,517864,7502
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/mint GATs for wrong validators/authority,17699300,53602,3852
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/mint GATs with bad token name/proposal,389689216,1074147,11012
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/mint GATs with bad token name/stake,201040711,510637,7097
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/wrong GAT datum/proposal,389689216,1074147,11184
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/wrong GAT datum/stake,201040711,510637,7269
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/wrong GAT datum/authority,15875303,48540,3619
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/invalid governor output datum/proposal,389689216,1074147,11190
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/invalid governor output datum/stake,201040711,510637,7275
Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/invalid governor output datum/authority,15875303,48540,3625
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from Draft to VotingReady/proposal,340836965,940834,10618
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from Draft to VotingReady/stake,144038574,373470,6509
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from VotingReady to Locked/proposal,390766816,1066406,10627
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from VotingReady to Locked/stake,144761110,375734,6516
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from Locked to Finished/proposal,436972923,1196415,11780
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from Locked to Finished/stake,201040711,510637,7669
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from Locked to Finished/governor,514789615,1314031,11627
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from Locked to Finished/authority,15875303,48540,4019
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to failed state/from Draft to Finished/proposal,338044229,934921,10620
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to failed state/from Draft to Finished/stake,144761110,375734,6511
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to failed state/from VotingReady to Finished/proposal,374688466,1021683,10621
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to failed state/from VotingReady to Finished/stake,144761110,375734,6512
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to failed state/from Locked to Finished/proposal,376950422,1026491,10621
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to failed state/from Locked to Finished/stake,144761110,375734,6512
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake,144038574,373470,6509
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake,144761110,375734,6516
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake,201040711,510637,7669
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/authority,15875303,48540,4019
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/insufficient cosigns/stake,144038574,373470,6509
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/insufficient votes/stake,144761110,375734,6512
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/ambiguous winning effect/stake,144761110,375734,6524
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/to next state too late/from Draft/stake,144038574,373470,6511
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/to next state too late/from VotingReady/stake,144761110,375734,6516
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/to next state too late/from Locked/stake,201040711,510637,7669
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/to next state too late/from Locked/governor,514789615,1314031,11627
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/to next state too late/from Locked/authority,15875303,48540,4019
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/altered output stake datum/from Locked/governor,514789615,1314031,11628
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/altered output stake datum/from Locked/authority,15875303,48540,4020
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/forget to mint GATs/proposal,412721877,1123806,11068
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/forget to mint GATs/stake,187940552,485026,6957
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/mint GATs for wrong validators/proposal,445056605,1220618,12007
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/mint GATs for wrong validators/stake,204588820,517864,7896
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/mint GATs for wrong validators/authority,17699300,53602,4246
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/mint GATs with bad token name/proposal,436972923,1196415,11601
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/mint GATs with bad token name/stake,201040711,510637,7490
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/wrong GAT datum/proposal,436972923,1196415,11774
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/wrong GAT datum/stake,201040711,510637,7663
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/wrong GAT datum/authority,15875303,48540,4013
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/invalid governor output datum/proposal,436972923,1196415,11780
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/invalid governor output datum/stake,201040711,510637,7669
Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/invalid governor output datum/authority,15875303,48540,4019
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from Draft to VotingReady/proposal,482688086,1307638,12390
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from Draft to VotingReady/stake,144038574,373470,7690
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from VotingReady to Locked/proposal,543702595,1463174,12399
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from VotingReady to Locked/stake,144761110,375734,7697
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from Locked to Finished/proposal,578824044,1563219,13551
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from Locked to Finished/stake,201040711,510637,8849
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from Locked to Finished/governor,576426487,1481542,12807
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from Locked to Finished/authority,15875303,48540,5199
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to failed state/from Draft to Finished/proposal,479895350,1301725,12392
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to failed state/from Draft to Finished/stake,144761110,375734,7692
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to failed state/from VotingReady to Finished/proposal,516539587,1388487,12393
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to failed state/from VotingReady to Finished/stake,144761110,375734,7693
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to failed state/from Locked to Finished/proposal,518801543,1393295,12393
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to failed state/from Locked to Finished/stake,144761110,375734,7693
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake,144038574,373470,7690
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake,144761110,375734,7697
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake,201040711,510637,8849
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/authority,15875303,48540,5199
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/insufficient cosigns/stake,144038574,373470,7690
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/insufficient votes/stake,144761110,375734,7693
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/ambiguous winning effect/stake,144761110,375734,7717
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/to next state too late/from Draft/stake,144038574,373470,7692
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/to next state too late/from VotingReady/stake,144761110,375734,7697
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/to next state too late/from Locked/stake,201040711,510637,8849
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/to next state too late/from Locked/governor,576426487,1481542,12807
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/to next state too late/from Locked/authority,15875303,48540,5199
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/altered output stake datum/from Locked/governor,576426487,1481542,12808
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/altered output stake datum/from Locked/authority,15875303,48540,5200
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/forget to mint GATs/proposal,554572998,1490610,12840
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/forget to mint GATs/stake,187940552,485026,8138
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/mint GATs for wrong validators/proposal,586907726,1587422,13778
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/mint GATs for wrong validators/stake,204588820,517864,9076
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/mint GATs for wrong validators/authority,17699300,53602,5426
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/mint GATs with bad token name/proposal,578824044,1563219,13373
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/mint GATs with bad token name/stake,201040711,510637,8671
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/wrong GAT datum/proposal,578824044,1563219,13545
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/wrong GAT datum/stake,201040711,510637,8843
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/wrong GAT datum/authority,15875303,48540,5193
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/invalid governor output datum/proposal,578824044,1563219,13551
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/invalid governor output datum/stake,201040711,510637,8849
Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/invalid governor output datum/authority,15875303,48540,5199
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from Draft to VotingReady/proposal,677995493,1927471,12799
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from Draft to VotingReady/stake,582713892,1530532,8765
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from VotingReady to Locked/proposal,362157571,996214,10395
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from VotingReady to Locked/stake,144761110,375734,6359
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from Locked to Finished/proposal,412058564,1136211,11547
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from Locked to Finished/stake,201040711,510637,7511
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from Locked to Finished/governor,501431939,1278882,11470
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from Locked to Finished/authority,15875303,48540,3862
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to failed state/from Draft to Finished/proposal,313129870,874717,10388
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to failed state/from Draft to Finished/stake,144761110,375734,6354
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to failed state/from VotingReady to Finished/proposal,349774107,961479,10389
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to failed state/from VotingReady to Finished/stake,144761110,375734,6355
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to failed state/from Locked to Finished/proposal,352036063,966287,10389
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to failed state/from Locked to Finished/stake,144761110,375734,6355
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake,582713892,1530532,8765
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake,144761110,375734,6359
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake,201040711,510637,7511
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/authority,15875303,48540,3862
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/insufficient cosigns/stake,568599606,1489522,8765
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/insufficient votes/stake,144761110,375734,6355
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/ambiguous winning effect/stake,144761110,375734,6363
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/to next state too late/from Draft/stake,582713892,1530532,8767
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/to next state too late/from VotingReady/stake,144761110,375734,6359
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/to next state too late/from Locked/stake,201040711,510637,7511
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/to next state too late/from Locked/governor,501431939,1278882,11470
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/to next state too late/from Locked/authority,15875303,48540,3862
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/altered output stake datum/from Locked/governor,501431939,1278882,11471
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/altered output stake datum/from Locked/authority,15875303,48540,3863
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/forget to mint GATs/proposal,387807518,1063602,10835
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/forget to mint GATs/stake,187940552,485026,6799
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/mint GATs for wrong validators/proposal,420142246,1160414,11774
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/mint GATs for wrong validators/stake,204588820,517864,7738
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/mint GATs for wrong validators/authority,17699300,53602,4089
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/mint GATs with bad token name/proposal,412058564,1136211,11369
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/mint GATs with bad token name/stake,201040711,510637,7333
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/wrong GAT datum/proposal,412058564,1136211,11541
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/wrong GAT datum/stake,201040711,510637,7505
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/wrong GAT datum/authority,15875303,48540,3856
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/invalid governor output datum/proposal,412058564,1136211,11547
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/invalid governor output datum/stake,201040711,510637,7511
Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/invalid governor output datum/authority,15875303,48540,3862
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from Draft to VotingReady/proposal,725279200,2049739,13390
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from Draft to VotingReady/stake,582713892,1530532,9159
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from VotingReady to Locked/proposal,413136164,1128470,10985
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from VotingReady to Locked/stake,144761110,375734,6752
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from Locked to Finished/proposal,459342271,1258479,12138
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from Locked to Finished/stake,201040711,510637,7905
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from Locked to Finished/governor,521977563,1334719,11864
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from Locked to Finished/authority,15875303,48540,4256
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to failed state/from Draft to Finished/proposal,360413577,996985,10978
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to failed state/from Draft to Finished/stake,144761110,375734,6747
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to failed state/from VotingReady to Finished/proposal,397057814,1083747,10979
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to failed state/from VotingReady to Finished/stake,144761110,375734,6748
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to failed state/from Locked to Finished/proposal,399319770,1088555,10979
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to failed state/from Locked to Finished/stake,144761110,375734,6748
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake,582713892,1530532,9159
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake,144761110,375734,6752
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake,201040711,510637,7905
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/authority,15875303,48540,4256
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/insufficient cosigns/stake,568599606,1489522,9159
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/insufficient votes/stake,144761110,375734,6748
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/ambiguous winning effect/stake,144761110,375734,6760
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/to next state too late/from Draft/stake,582713892,1530532,9161
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/to next state too late/from VotingReady/stake,144761110,375734,6752
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/to next state too late/from Locked/stake,201040711,510637,7905
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/to next state too late/from Locked/governor,521977563,1334719,11864
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/to next state too late/from Locked/authority,15875303,48540,4256
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/altered output stake datum/from Locked/governor,521977563,1334719,11865
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/altered output stake datum/from Locked/authority,15875303,48540,4257
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/forget to mint GATs/proposal,435091225,1185870,11426
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/forget to mint GATs/stake,187940552,485026,7193
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/mint GATs for wrong validators/proposal,467425953,1282682,12365
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/mint GATs for wrong validators/stake,204588820,517864,8132
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/mint GATs for wrong validators/authority,17699300,53602,4483
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/mint GATs with bad token name/proposal,459342271,1258479,11959
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/mint GATs with bad token name/stake,201040711,510637,7726
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/wrong GAT datum/proposal,459342271,1258479,12132
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/wrong GAT datum/stake,201040711,510637,7899
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/wrong GAT datum/authority,15875303,48540,4250
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/invalid governor output datum/proposal,459342271,1258479,12138
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/invalid governor output datum/stake,201040711,510637,7905
Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/invalid governor output datum/authority,15875303,48540,4256
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from Draft to VotingReady/proposal,867130321,2416543,15160
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from Draft to VotingReady/stake,582713892,1530532,10339
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from VotingReady to Locked/proposal,566071943,1525238,12756
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from VotingReady to Locked/stake,144761110,375734,7933
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from Locked to Finished/proposal,601193392,1625283,13908
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from Locked to Finished/stake,201040711,510637,9085
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from Locked to Finished/governor,583614435,1502230,13044
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from Locked to Finished/authority,15875303,48540,5436
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to failed state/from Draft to Finished/proposal,502264698,1363789,12749
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to failed state/from Draft to Finished/stake,144761110,375734,7928
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to failed state/from VotingReady to Finished/proposal,538908935,1450551,12750
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to failed state/from VotingReady to Finished/stake,144761110,375734,7929
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to failed state/from Locked to Finished/proposal,541170891,1455359,12750
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to failed state/from Locked to Finished/stake,144761110,375734,7929
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake,582713892,1530532,10339
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake,144761110,375734,7933
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake,201040711,510637,9085
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/authority,15875303,48540,5436
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/insufficient cosigns/stake,568599606,1489522,10339
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/insufficient votes/stake,144761110,375734,7929
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/ambiguous winning effect/stake,144761110,375734,7953
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/to next state too late/from Draft/stake,582713892,1530532,10341
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/to next state too late/from VotingReady/stake,144761110,375734,7933
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/to next state too late/from Locked/stake,201040711,510637,9085
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/to next state too late/from Locked/governor,583614435,1502230,13044
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/to next state too late/from Locked/authority,15875303,48540,5436
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/altered output stake datum/from Locked/governor,583614435,1502230,13045
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/altered output stake datum/from Locked/authority,15875303,48540,5437
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/forget to mint GATs/proposal,576942346,1552674,13197
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/forget to mint GATs/stake,187940552,485026,8374
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/mint GATs for wrong validators/proposal,609277074,1649486,14135
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/mint GATs for wrong validators/stake,204588820,517864,9312
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/mint GATs for wrong validators/authority,17699300,53602,5663
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/mint GATs with bad token name/proposal,601193392,1625283,13730
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/mint GATs with bad token name/stake,201040711,510637,8907
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/wrong GAT datum/proposal,601193392,1625283,13902
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/wrong GAT datum/stake,201040711,510637,9079
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/wrong GAT datum/authority,15875303,48540,5430
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/invalid governor output datum/proposal,601193392,1625283,13908
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/invalid governor output datum/stake,201040711,510637,9085
Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/invalid governor output datum/authority,15875303,48540,5436
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from Draft to VotingReady/proposal,1245892718,3569074,16268
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from Draft to VotingReady/stake,1243243041,3300429,12083
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from VotingReady to Locked/proposal,390119256,1073794,10847
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from VotingReady to Locked/stake,144761110,375734,6660
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from Locked to Finished/proposal,440020249,1213791,11999
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from Locked to Finished/stake,201040711,510637,7812
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from Locked to Finished/governor,510416874,1304742,11771
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from Locked to Finished/authority,15875303,48540,4163
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to failed state/from Draft to Finished/proposal,341091555,952297,10840
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to failed state/from Draft to Finished/stake,144761110,375734,6655
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to failed state/from VotingReady to Finished/proposal,377735792,1039059,10841
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to failed state/from VotingReady to Finished/stake,144761110,375734,6656
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to failed state/from Locked to Finished/proposal,379997748,1043867,10841
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to failed state/from Locked to Finished/stake,144761110,375734,6656
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake,1243243041,3300429,12083
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake,144761110,375734,6660
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake,201040711,510637,7812
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/authority,15875303,48540,4163
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/insufficient cosigns/stake,1257357327,3341439,12083
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/insufficient votes/stake,144761110,375734,6656
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/ambiguous winning effect/stake,144761110,375734,6664
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/to next state too late/from Draft/stake,1243243041,3300429,12085
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/to next state too late/from VotingReady/stake,144761110,375734,6660
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/to next state too late/from Locked/stake,201040711,510637,7812
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/to next state too late/from Locked/governor,510416874,1304742,11771
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/to next state too late/from Locked/authority,15875303,48540,4163
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/altered output stake datum/from Locked/governor,510416874,1304742,11772
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/altered output stake datum/from Locked/authority,15875303,48540,4164
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/forget to mint GATs/proposal,415769203,1141182,11288
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/forget to mint GATs/stake,187940552,485026,7101
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/mint GATs for wrong validators/proposal,448103931,1237994,12226
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/mint GATs for wrong validators/stake,204588820,517864,8039
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/mint GATs for wrong validators/authority,17699300,53602,4390
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/mint GATs with bad token name/proposal,440020249,1213791,11821
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/mint GATs with bad token name/stake,201040711,510637,7634
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/wrong GAT datum/proposal,440020249,1213791,11993
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/wrong GAT datum/stake,201040711,510637,7806
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/wrong GAT datum/authority,15875303,48540,4157
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/invalid governor output datum/proposal,440020249,1213791,11999
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/invalid governor output datum/stake,201040711,510637,7812
Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/invalid governor output datum/authority,15875303,48540,4163
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from Draft to VotingReady/proposal,1293176425,3691342,16858
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from Draft to VotingReady/stake,1243243041,3300429,12477
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from VotingReady to Locked/proposal,441097849,1206050,11436
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from VotingReady to Locked/stake,144761110,375734,7053
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from Locked to Finished/proposal,487303956,1336059,12589
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from Locked to Finished/stake,201040711,510637,8206
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from Locked to Finished/governor,530962498,1360579,12165
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from Locked to Finished/authority,15875303,48540,4557
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to failed state/from Draft to Finished/proposal,388375262,1074565,11429
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to failed state/from Draft to Finished/stake,144761110,375734,7048
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to failed state/from VotingReady to Finished/proposal,425019499,1161327,11430
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to failed state/from VotingReady to Finished/stake,144761110,375734,7049
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to failed state/from Locked to Finished/proposal,427281455,1166135,11430
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to failed state/from Locked to Finished/stake,144761110,375734,7049
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake,1243243041,3300429,12477
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake,144761110,375734,7053
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake,201040711,510637,8206
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/authority,15875303,48540,4557
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/insufficient cosigns/stake,1257357327,3341439,12477
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/insufficient votes/stake,144761110,375734,7049
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/ambiguous winning effect/stake,144761110,375734,7061
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/to next state too late/from Draft/stake,1243243041,3300429,12479
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/to next state too late/from VotingReady/stake,144761110,375734,7053
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/to next state too late/from Locked/stake,201040711,510637,8206
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/to next state too late/from Locked/governor,530962498,1360579,12165
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/to next state too late/from Locked/authority,15875303,48540,4557
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/altered output stake datum/from Locked/governor,530962498,1360579,12166
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/altered output stake datum/from Locked/authority,15875303,48540,4558
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/forget to mint GATs/proposal,463052910,1263450,11877
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/forget to mint GATs/stake,187940552,485026,7494
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/mint GATs for wrong validators/proposal,495387638,1360262,12816
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/mint GATs for wrong validators/stake,204588820,517864,8433
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/mint GATs for wrong validators/authority,17699300,53602,4784
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/mint GATs with bad token name/proposal,487303956,1336059,12410
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/mint GATs with bad token name/stake,201040711,510637,8027
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/wrong GAT datum/proposal,487303956,1336059,12583
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/wrong GAT datum/stake,201040711,510637,8200
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/wrong GAT datum/authority,15875303,48540,4551
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/invalid governor output datum/proposal,487303956,1336059,12589
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/invalid governor output datum/stake,201040711,510637,8206
Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/invalid governor output datum/authority,15875303,48540,4557
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from Draft to VotingReady/proposal,1435027546,4058146,18629
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from Draft to VotingReady/stake,1243243041,3300429,13657
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from VotingReady to Locked/proposal,594033628,1602818,13208
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from VotingReady to Locked/stake,144761110,375734,8234
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from Locked to Finished/proposal,629155077,1702863,14361
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from Locked to Finished/stake,201040711,510637,9387
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from Locked to Finished/governor,592599370,1528090,13346
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from Locked to Finished/authority,15875303,48540,5738
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to failed state/from Draft to Finished/proposal,530226383,1441369,13201
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to failed state/from Draft to Finished/stake,144761110,375734,8229
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to failed state/from VotingReady to Finished/proposal,566870620,1528131,13202
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to failed state/from VotingReady to Finished/stake,144761110,375734,8230
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to failed state/from Locked to Finished/proposal,569132576,1532939,13202
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to failed state/from Locked to Finished/stake,144761110,375734,8230
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake,1243243041,3300429,13657
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake,144761110,375734,8234
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake,201040711,510637,9387
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/authority,15875303,48540,5738
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/insufficient cosigns/stake,1257357327,3341439,13657
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/insufficient votes/stake,144761110,375734,8230
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/ambiguous winning effect/stake,144761110,375734,8254
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/to next state too late/from Draft/stake,1243243041,3300429,13659
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/to next state too late/from VotingReady/stake,144761110,375734,8234
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/to next state too late/from Locked/stake,201040711,510637,9387
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/to next state too late/from Locked/governor,592599370,1528090,13346
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/to next state too late/from Locked/authority,15875303,48540,5738
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/altered output stake datum/from Locked/governor,592599370,1528090,13347
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/altered output stake datum/from Locked/authority,15875303,48540,5739
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/forget to mint GATs/proposal,604904031,1630254,13649
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/forget to mint GATs/stake,187940552,485026,8675
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/mint GATs for wrong validators/proposal,637238759,1727066,14587
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/mint GATs for wrong validators/stake,204588820,517864,9613
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/mint GATs for wrong validators/authority,17699300,53602,5964
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/mint GATs with bad token name/proposal,629155077,1702863,14182
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/mint GATs with bad token name/stake,201040711,510637,9208
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/wrong GAT datum/proposal,629155077,1702863,14355
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/wrong GAT datum/stake,201040711,510637,9381
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/wrong GAT datum/authority,15875303,48540,5731
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/invalid governor output datum/proposal,629155077,1702863,14361
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/invalid governor output datum/stake,201040711,510637,9387
Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/invalid governor output datum/authority,15875303,48540,5738
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: retract votes while voting/stake,140863709,370195,5783
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: retract votes while voting/proposal,282615841,794299,9497
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: retract votes while voting/stake,147212927,388303,5799
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: retract votes while voting/proposal,300115413,843740,9508
Agora/Proposal/validator/unlocking/legal/with 1 proposals/creator: remove creator locks when finished/stake,139825432,367397,5781
Agora/Proposal/validator/unlocking/legal/with 1 proposals/creator: remove creator locks when finished/proposal,256687085,727957,9494
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove all locks when finished/stake,143981681,379249,5797
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove all locks when finished/proposal,264924554,752465,9506
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Locked/stake,140863709,370195,5787
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Locked/proposal,252614193,719732,9501
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Finished/stake,140863709,370195,5787
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Finished/proposal,253179682,720934,9501
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove vote locks when locked/stake,147212927,388303,5803
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove vote locks when locked/proposal,270679254,770375,9512
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: retract votes while voting/stake,297243845,781199,7876
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: retract votes while voting/proposal,425257089,1206963,11566
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: retract votes while voting/stake,328989935,871739,7952
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: retract votes while voting/proposal,494812989,1407716,11617
Agora/Proposal/validator/unlocking/legal/with 5 proposals/creator: remove creator locks when finished/stake,292052460,767209,7865
Agora/Proposal/validator/unlocking/legal/with 5 proposals/creator: remove creator locks when finished/proposal,364767897,1045813,11558
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove all locks when finished/stake,312833705,826469,7946
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove all locks when finished/proposal,402960090,1157169,11611
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Locked/stake,297243845,781199,7896
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Locked/proposal,364848113,1048780,11586
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Finished/stake,297243845,781199,7896
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Finished/proposal,365413602,1049982,11586
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove vote locks when locked/stake,328989935,871739,7972
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove vote locks when locked/proposal,434969502,1250735,11637
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: retract votes while voting/stake,492719015,1294954,10491
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: retract votes while voting/proposal,603558649,1722793,14151
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: retract votes while voting/stake,556211195,1476034,10642
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: retract votes while voting/proposal,738184959,2112686,14252
Agora/Proposal/validator/unlocking/legal/with 10 proposals/creator: remove creator locks when finished/stake,482336245,1266974,10471
Agora/Proposal/validator/unlocking/legal/with 10 proposals/creator: remove creator locks when finished/proposal,499868912,1443133,14139
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove all locks when finished/stake,523898735,1385494,10631
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove all locks when finished/proposal,575504510,1663049,14241
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Locked/stake,492719015,1294954,10531
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Locked/proposal,505140513,1460090,14191
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Finished/stake,492719015,1294954,10531
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Finished/proposal,505706002,1461292,14191
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove vote locks when locked/stake,556211195,1476034,10682
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove vote locks when locked/proposal,640332312,1851185,14292
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: retract votes while voting/stake,1743760103,4582986,27321
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: retract votes while voting/proposal,1744688633,5024105,30770
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: retract votes while voting/stake,2010427259,5343522,28008
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: retract votes while voting/proposal,2295765567,6624494,31228
Agora/Proposal/validator/unlocking/legal/with 42 proposals/creator: remove creator locks when finished/stake,1700152469,4465470,27236
Agora/Proposal/validator/unlocking/legal/with 42 proposals/creator: remove creator locks when finished/proposal,1364515408,3985981,30725
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove all locks when finished/stake,1874714927,4963254,27947
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove all locks when finished/proposal,1679788798,4900681,31167
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Locked/stake,1743760103,4582986,27489
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Locked/proposal,1403011873,4092474,30938
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Finished/stake,1743760103,4582986,27489
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Finished/proposal,1403577362,4093676,30938
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove vote locks when locked/stake,2010427259,5343522,28177
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove vote locks when locked/proposal,1954654296,5694065,31397
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter , status: Draft/stake",140863709,370195,5783
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter , status: Locked/stake",140863709,370195,5783
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter , status: Finished/stake",140863709,370195,5783
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator , status: Draft/stake",135454846,355547,5785
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator , status: Locked/stake",135454846,355547,5785
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator , status: Finished/stake",135454846,355547,5785
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both , status: Draft/stake",147212927,388303,5799
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both , status: Locked/stake",147212927,388303,5799
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both , status: Finished/stake",147212927,388303,5799
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake",129105628,337439,5765
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake",129105628,337439,5765
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake",129105628,337439,5765
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake,129105628,337439,5765
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake,129105628,337439,5765
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake,129105628,337439,5765
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake,129105628,337439,5765
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake,129105628,337439,5765
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake,129105628,337439,5765
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake,129105628,337439,5765
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake,129105628,337439,5765
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: Draft/stake,139825432,367397,5781
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: VotingReady/stake,139825432,367397,5781
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: Locked/stake,139825432,367397,5781
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/creator: retract votes/stake,139825432,367397,5779
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter , status: Draft/stake",297243845,781199,7876
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter , status: Locked/stake",297243845,781199,7876
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter , status: Finished/stake",297243845,781199,7876
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator , status: Draft/stake",285403194,749767,7882
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator , status: Locked/stake",285403194,749767,7882
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator , status: Finished/stake",285403194,749767,7882
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both , status: Draft/stake",328989935,871739,7952
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both , status: Locked/stake",328989935,871739,7952
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both , status: Finished/stake",328989935,871739,7952
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake",253657104,659227,7793
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake",253657104,659227,7793
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake",253657104,659227,7793
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake,253657104,659227,7793
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake,253657104,659227,7793
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake,253657104,659227,7793
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake,253657104,659227,7793
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake,253657104,659227,7793
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake,253657104,659227,7793
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake,253657104,659227,7793
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake,253657104,659227,7793
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: Draft/stake,292052460,767209,7865
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: VotingReady/stake,292052460,767209,7865
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: Locked/stake,292052460,767209,7865
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/creator: retract votes/stake,292052460,767209,7855
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter , status: Draft/stake",492719015,1294954,10491
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter , status: Locked/stake",492719015,1294954,10491
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter , status: Finished/stake",492719015,1294954,10491
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator , status: Draft/stake",472838629,1242542,10502
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator , status: Locked/stake",472838629,1242542,10502
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator , status: Finished/stake",472838629,1242542,10502
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both , status: Draft/stake",556211195,1476034,10642
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both , status: Locked/stake",556211195,1476034,10642
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both , status: Finished/stake",556211195,1476034,10642
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake",409346449,1061462,10328
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake",409346449,1061462,10328
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake",409346449,1061462,10328
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake,409346449,1061462,10328
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake,409346449,1061462,10328
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake,409346449,1061462,10328
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake,409346449,1061462,10328
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake,409346449,1061462,10328
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake,409346449,1061462,10328
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake,409346449,1061462,10328
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake,409346449,1061462,10328
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: Draft/stake,482336245,1266974,10471
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: VotingReady/stake,482336245,1266974,10471
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: Locked/stake,482336245,1266974,10471
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/creator: retract votes/stake,482336245,1266974,10450
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter , status: Draft/stake",1743760103,4582986,27321
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter , status: Locked/stake",1743760103,4582986,27321
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter , status: Finished/stake",1743760103,4582986,27321
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator , status: Draft/stake",1672425413,4396302,27382
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator , status: Locked/stake",1672425413,4396302,27382
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator , status: Finished/stake",1672425413,4396302,27382
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both , status: Draft/stake",2010427259,5343522,28008
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both , status: Locked/stake",2010427259,5343522,28008
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both , status: Finished/stake",2010427259,5343522,28008
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake",1405758257,3635766,26608
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake",1405758257,3635766,26608
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake",1405758257,3635766,26608
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake,1405758257,3635766,26608
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake,1405758257,3635766,26608
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake,1405758257,3635766,26608
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake,1405758257,3635766,26608
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake,1405758257,3635766,26608
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake,1405758257,3635766,26608
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake,1405758257,3635766,26608
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake,1405758257,3635766,26608
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: Draft/stake,1700152469,4465470,27236
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: VotingReady/stake,1700152469,4465470,27236
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: Locked/stake,1700152469,4465470,27236
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/creator: retract votes/stake,1700152469,4465470,27152
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,19822997,52452,427
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,32009395,84810,527
Agora/Treasury/Validator/Positive/Allows for effect changes,31553082,81982,1423
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,19822997,52452,427
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,32009395,84810,527
Agora/Governor/policy/totally legal,70301827,197578,2576
Agora/Governor/validator/mutate/legal,119848510,323653,9297

1 name cpu mem size
2 Agora/Effects/Treasury Withdrawal Effect/effect/Simple 333327612 380214695 830203 980182 3674 4275
3 Agora/Effects/Treasury Withdrawal Effect/effect/Simple with multiple treasuries 492387542 544143721 1197315 1366494 3986 4691
4 Agora/Effects/Treasury Withdrawal Effect/effect/Mixed Assets 456007605 545045362 1104500 1387355 3859 4636
5 Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/governor validator should pass 90397270 112721874 249528 312363 8807 9413
6 Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/effect validator should pass 106082031 152563857 292993 422397 3609 4692
7 Agora/Stake/policy/stakeCreation 52241265 54323406 152127 159125 2514 2646
8 Agora/Stake/validator/stakeDepositWithdraw deposit 180880812 194804304 492023 538628 4431 5315
9 Agora/Stake/validator/stakeDepositWithdraw withdraw 180880812 194804304 492023 538628 4419 5303
10 Agora/Proposal/policy/proposalCreation Agora/Stake/validator/set delegate/override existing delegate 23140177 117949415 69194 309090 1519 5367
11 Agora/Proposal/validator/cosignature/proposal Agora/Stake/validator/set delegate/remove existing delegate 338483402 115539973 961112 301757 8620 5304
12 Agora/Proposal/validator/cosignature/stake Agora/Stake/validator/set delegate/set delegate to something 126327509 114218077 315061 301022 4968 5304
13 Agora/Proposal/validator/voting/proposal Agora/Proposal/policy (proposal creation)/legal/proposal 296656410 33965644 830692 101486 8549 1971
14 Agora/Proposal/validator/voting/stake Agora/Proposal/policy (proposal creation)/legal/governor 121170376 369498544 320853 984529 4942 9918
15 Agora/Proposal/validator/advancing/successfully advance to next state/Draft -> VotringReady Agora/Proposal/policy (proposal creation)/legal/stake 294340341 168978875 825452 446628 8447 5969
16 Agora/Proposal/validator/advancing/successfully advance to next state/VotingReady -> Locked Agora/Proposal/policy (proposal creation)/illegal/invalid next proposal id/proposal 306801371 33965644 861382 101486 8456 1971
17 Agora/Proposal/validator/advancing/successfully advance to next state/Locked -> Finished Agora/Proposal/policy (proposal creation)/illegal/invalid next proposal id/stake 295193386 168978875 827555 446628 8456 5969
18 Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Draft -> Finished Agora/Proposal/policy (proposal creation)/illegal/use other's stake/proposal 293210540 33965644 822722 101486 8449 1940
19 Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/VotingReady -> Finished Agora/Proposal/policy (proposal creation)/illegal/use other's stake/governor 291801629 369498544 820017 984529 8450 9887
20 Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Locked -> Finished Agora/Proposal/policy (proposal creation)/illegal/altered stake/proposal 292932607 33965644 822421 101486 8450 1971
21 Agora/Proposal/validator/unlocking/legal/1 proposals, voter, unlock stake + retract votes, VotingReady Agora/Proposal/policy (proposal creation)/illegal/invalid stake locks/proposal 302502183 33965644 848154 101486 8500 1979
22 Agora/Proposal/validator/unlocking/legal/1 proposals, creator, unlock stake, Finished Agora/Proposal/policy (proposal creation)/illegal/invalid stake locks/stake 273224492 174412535 773388 461278 8504 5977
23 Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/1 proposals, voter, unlock stake, Finished Agora/Proposal/policy (proposal creation)/illegal/has reached maximum proposals limit/proposal 268700821 33965644 763033 101486 8504 1991
24 Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/1 proposals, voter, unlock stake, Locked Agora/Proposal/policy (proposal creation)/illegal/has reached maximum proposals limit/stake 268700821 181677311 763033 482844 8504 5999
25 Agora/Proposal/validator/unlocking/legal/42 proposals, voter, unlock stake + retract votes, VotingReady Agora/Proposal/policy (proposal creation)/illegal/loose time range/proposal 2908014422 33965644 8180225 101486 30018 1971
26 Agora/Proposal/validator/unlocking/legal/42 proposals, creator, unlock stake, Finished Agora/Proposal/policy (proposal creation)/illegal/loose time range/stake 2616129517 168978875 7383326 446628 30287 5969
27 Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/42 proposals, voter, unlock stake, Finished Agora/Proposal/policy (proposal creation)/illegal/open time range/proposal 2464384686 33965644 6936321 101486 30187 1967
28 Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/42 proposals, voter, unlock stake, Locked Agora/Proposal/policy (proposal creation)/illegal/open time range/stake 2464384686 168978875 6936321 446628 30187 5965
29 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/VotingReady/proposal 21017788 33965644 55883 101486 806 1971
30 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/VotingReady/stake 33204186 168978875 88241 446628 900 5969
31 Agora/Treasury/Validator/Positive/Allows for effect changes Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Locked/proposal 29938856 33965644 79744 101486 1391 1971
32 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Locked/stake 21017788 168978875 55883 446628 806 5969
33 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Finished/proposal 33204186 33965644 88241 101486 900 1971
34 Agora/Governor/policy/GST minting Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Finished/stake 51007235 168978875 144191 446628 2034 5969
35 Agora/Governor/validator/proposal creation Agora/Proposal/validator/cosignature/legal/with 1 cosigners/proposal 317651809 278329834 854963 780402 9323 9534
36 Agora/Governor/validator/GATs minting Agora/Proposal/validator/cosignature/legal/with 1 cosigners/stake 423756405 132939473 1151000 344002 9444 5780
37 Agora/Governor/validator/mutate governor state Agora/Proposal/validator/cosignature/legal/with 5 cosigners/proposal 91544121 733369909 254987 2047977 8908 12188
38 Agora/Proposal/validator/cosignature/legal/with 5 cosigners/stake 584246489 1537942 8314
39 Agora/Proposal/validator/cosignature/legal/with 10 cosigners/proposal 1453961927 4008580 15507
40 Agora/Proposal/validator/cosignature/legal/with 10 cosigners/stake 1244034344 3305773 11482
41 Agora/Proposal/validator/cosignature/illegal/duplicate cosigners/stake 132939473 344002 5780
42 Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: VotingReady/stake 132939473 344002 5780
43 Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: Locked/stake 132939473 344002 5780
44 Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: Finished/stake 132939473 344002 5780
45 Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: VotingReady/stake 584246489 1537942 8314
46 Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: Locked/stake 584246489 1537942 8314
47 Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: Finished/stake 584246489 1537942 8314
48 Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: VotingReady/stake 1244034344 3305773 11482
49 Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: Locked/stake 1244034344 3305773 11482
50 Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: Finished/stake 1244034344 3305773 11482
51 Agora/Proposal/validator/voting/legal/ordinary/proposal 300100170 843476 9516
52 Agora/Proposal/validator/voting/legal/ordinary/stake 157219895 416300 5803
53 Agora/Proposal/validator/voting/legal/delegate/proposal 301216160 847088 9579
54 Agora/Proposal/validator/voting/legal/delegate/stake 161458884 426174 5897
55 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from Draft to VotingReady/proposal 293553258 818566 10029
56 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from Draft to VotingReady/stake 144038574 373470 6116
57 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from VotingReady to Locked/proposal 339788223 934150 10038
58 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from VotingReady to Locked/stake 144761110 375734 6123
59 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from Locked to Finished/proposal 389689216 1074147 11190
60 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from Locked to Finished/stake 201040711 510637 7275
61 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from Locked to Finished/governor 494243991 1258194 11233
62 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from Locked to Finished/authority 15875303 48540 3625
63 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to failed state/from Draft to Finished/proposal 290760522 812653 10031
64 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to failed state/from Draft to Finished/stake 144761110 375734 6118
65 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to failed state/from VotingReady to Finished/proposal 327404759 899415 10032
66 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to failed state/from VotingReady to Finished/stake 144761110 375734 6119
67 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to failed state/from Locked to Finished/proposal 329666715 904223 10032
68 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to failed state/from Locked to Finished/stake 144761110 375734 6119
69 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake 144038574 373470 6116
70 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake 144761110 375734 6123
71 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake 201040711 510637 7275
72 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/authority 15875303 48540 3625
73 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/insufficient cosigns/stake 144038574 373470 6116
74 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/insufficient votes/stake 144761110 375734 6119
75 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/ambiguous winning effect/stake 144761110 375734 6127
76 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/to next state too late/from Draft/stake 144038574 373470 6118
77 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/to next state too late/from VotingReady/stake 144761110 375734 6123
78 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/to next state too late/from Locked/stake 201040711 510637 7275
79 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/to next state too late/from Locked/governor 494243991 1258194 11233
80 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/to next state too late/from Locked/authority 15875303 48540 3625
81 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/altered output stake datum/from Locked/governor 494243991 1258194 11234
82 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/altered output stake datum/from Locked/authority 15875303 48540 3626
83 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/forget to mint GATs/proposal 365438170 1001538 10479
84 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/forget to mint GATs/stake 187940552 485026 6564
85 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/mint GATs for wrong validators/proposal 397772898 1098350 11417
86 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/mint GATs for wrong validators/stake 204588820 517864 7502
87 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/mint GATs for wrong validators/authority 17699300 53602 3852
88 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/mint GATs with bad token name/proposal 389689216 1074147 11012
89 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/mint GATs with bad token name/stake 201040711 510637 7097
90 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/wrong GAT datum/proposal 389689216 1074147 11184
91 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/wrong GAT datum/stake 201040711 510637 7269
92 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/wrong GAT datum/authority 15875303 48540 3619
93 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/invalid governor output datum/proposal 389689216 1074147 11190
94 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/invalid governor output datum/stake 201040711 510637 7275
95 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/illegal/invalid governor output datum/authority 15875303 48540 3625
96 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from Draft to VotingReady/proposal 340836965 940834 10618
97 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from Draft to VotingReady/stake 144038574 373470 6509
98 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from VotingReady to Locked/proposal 390766816 1066406 10627
99 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from VotingReady to Locked/stake 144761110 375734 6516
100 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from Locked to Finished/proposal 436972923 1196415 11780
101 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from Locked to Finished/stake 201040711 510637 7669
102 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from Locked to Finished/governor 514789615 1314031 11627
103 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to next state/from Locked to Finished/authority 15875303 48540 4019
104 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to failed state/from Draft to Finished/proposal 338044229 934921 10620
105 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to failed state/from Draft to Finished/stake 144761110 375734 6511
106 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to failed state/from VotingReady to Finished/proposal 374688466 1021683 10621
107 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to failed state/from VotingReady to Finished/stake 144761110 375734 6512
108 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to failed state/from Locked to Finished/proposal 376950422 1026491 10621
109 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/legal/to failed state/from Locked to Finished/stake 144761110 375734 6512
110 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake 144038574 373470 6509
111 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake 144761110 375734 6516
112 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake 201040711 510637 7669
113 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/authority 15875303 48540 4019
114 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/insufficient cosigns/stake 144038574 373470 6509
115 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/insufficient votes/stake 144761110 375734 6512
116 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/ambiguous winning effect/stake 144761110 375734 6524
117 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/to next state too late/from Draft/stake 144038574 373470 6511
118 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/to next state too late/from VotingReady/stake 144761110 375734 6516
119 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/to next state too late/from Locked/stake 201040711 510637 7669
120 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/to next state too late/from Locked/governor 514789615 1314031 11627
121 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/to next state too late/from Locked/authority 15875303 48540 4019
122 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/altered output stake datum/from Locked/governor 514789615 1314031 11628
123 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/altered output stake datum/from Locked/authority 15875303 48540 4020
124 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/forget to mint GATs/proposal 412721877 1123806 11068
125 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/forget to mint GATs/stake 187940552 485026 6957
126 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/mint GATs for wrong validators/proposal 445056605 1220618 12007
127 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/mint GATs for wrong validators/stake 204588820 517864 7896
128 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/mint GATs for wrong validators/authority 17699300 53602 4246
129 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/mint GATs with bad token name/proposal 436972923 1196415 11601
130 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/mint GATs with bad token name/stake 201040711 510637 7490
131 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/wrong GAT datum/proposal 436972923 1196415 11774
132 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/wrong GAT datum/stake 201040711 510637 7663
133 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/wrong GAT datum/authority 15875303 48540 4013
134 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/invalid governor output datum/proposal 436972923 1196415 11780
135 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/invalid governor output datum/stake 201040711 510637 7669
136 Agora/Proposal/validator/advancing/with 1 cosigners and 2 effects/illegal/invalid governor output datum/authority 15875303 48540 4019
137 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from Draft to VotingReady/proposal 482688086 1307638 12390
138 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from Draft to VotingReady/stake 144038574 373470 7690
139 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from VotingReady to Locked/proposal 543702595 1463174 12399
140 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from VotingReady to Locked/stake 144761110 375734 7697
141 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from Locked to Finished/proposal 578824044 1563219 13551
142 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from Locked to Finished/stake 201040711 510637 8849
143 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from Locked to Finished/governor 576426487 1481542 12807
144 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to next state/from Locked to Finished/authority 15875303 48540 5199
145 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to failed state/from Draft to Finished/proposal 479895350 1301725 12392
146 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to failed state/from Draft to Finished/stake 144761110 375734 7692
147 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to failed state/from VotingReady to Finished/proposal 516539587 1388487 12393
148 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to failed state/from VotingReady to Finished/stake 144761110 375734 7693
149 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to failed state/from Locked to Finished/proposal 518801543 1393295 12393
150 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/legal/to failed state/from Locked to Finished/stake 144761110 375734 7693
151 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake 144038574 373470 7690
152 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake 144761110 375734 7697
153 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake 201040711 510637 8849
154 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/authority 15875303 48540 5199
155 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/insufficient cosigns/stake 144038574 373470 7690
156 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/insufficient votes/stake 144761110 375734 7693
157 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/ambiguous winning effect/stake 144761110 375734 7717
158 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/to next state too late/from Draft/stake 144038574 373470 7692
159 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/to next state too late/from VotingReady/stake 144761110 375734 7697
160 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/to next state too late/from Locked/stake 201040711 510637 8849
161 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/to next state too late/from Locked/governor 576426487 1481542 12807
162 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/to next state too late/from Locked/authority 15875303 48540 5199
163 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/altered output stake datum/from Locked/governor 576426487 1481542 12808
164 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/altered output stake datum/from Locked/authority 15875303 48540 5200
165 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/forget to mint GATs/proposal 554572998 1490610 12840
166 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/forget to mint GATs/stake 187940552 485026 8138
167 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/mint GATs for wrong validators/proposal 586907726 1587422 13778
168 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/mint GATs for wrong validators/stake 204588820 517864 9076
169 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/mint GATs for wrong validators/authority 17699300 53602 5426
170 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/mint GATs with bad token name/proposal 578824044 1563219 13373
171 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/mint GATs with bad token name/stake 201040711 510637 8671
172 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/wrong GAT datum/proposal 578824044 1563219 13545
173 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/wrong GAT datum/stake 201040711 510637 8843
174 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/wrong GAT datum/authority 15875303 48540 5193
175 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/invalid governor output datum/proposal 578824044 1563219 13551
176 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/invalid governor output datum/stake 201040711 510637 8849
177 Agora/Proposal/validator/advancing/with 1 cosigners and 5 effects/illegal/invalid governor output datum/authority 15875303 48540 5199
178 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from Draft to VotingReady/proposal 677995493 1927471 12799
179 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from Draft to VotingReady/stake 582713892 1530532 8765
180 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from VotingReady to Locked/proposal 362157571 996214 10395
181 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from VotingReady to Locked/stake 144761110 375734 6359
182 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from Locked to Finished/proposal 412058564 1136211 11547
183 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from Locked to Finished/stake 201040711 510637 7511
184 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from Locked to Finished/governor 501431939 1278882 11470
185 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to next state/from Locked to Finished/authority 15875303 48540 3862
186 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to failed state/from Draft to Finished/proposal 313129870 874717 10388
187 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to failed state/from Draft to Finished/stake 144761110 375734 6354
188 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to failed state/from VotingReady to Finished/proposal 349774107 961479 10389
189 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to failed state/from VotingReady to Finished/stake 144761110 375734 6355
190 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to failed state/from Locked to Finished/proposal 352036063 966287 10389
191 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/legal/to failed state/from Locked to Finished/stake 144761110 375734 6355
192 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake 582713892 1530532 8765
193 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake 144761110 375734 6359
194 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake 201040711 510637 7511
195 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/authority 15875303 48540 3862
196 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/insufficient cosigns/stake 568599606 1489522 8765
197 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/insufficient votes/stake 144761110 375734 6355
198 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/ambiguous winning effect/stake 144761110 375734 6363
199 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/to next state too late/from Draft/stake 582713892 1530532 8767
200 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/to next state too late/from VotingReady/stake 144761110 375734 6359
201 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/to next state too late/from Locked/stake 201040711 510637 7511
202 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/to next state too late/from Locked/governor 501431939 1278882 11470
203 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/to next state too late/from Locked/authority 15875303 48540 3862
204 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/altered output stake datum/from Locked/governor 501431939 1278882 11471
205 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/altered output stake datum/from Locked/authority 15875303 48540 3863
206 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/forget to mint GATs/proposal 387807518 1063602 10835
207 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/forget to mint GATs/stake 187940552 485026 6799
208 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/mint GATs for wrong validators/proposal 420142246 1160414 11774
209 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/mint GATs for wrong validators/stake 204588820 517864 7738
210 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/mint GATs for wrong validators/authority 17699300 53602 4089
211 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/mint GATs with bad token name/proposal 412058564 1136211 11369
212 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/mint GATs with bad token name/stake 201040711 510637 7333
213 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/wrong GAT datum/proposal 412058564 1136211 11541
214 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/wrong GAT datum/stake 201040711 510637 7505
215 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/wrong GAT datum/authority 15875303 48540 3856
216 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/invalid governor output datum/proposal 412058564 1136211 11547
217 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/invalid governor output datum/stake 201040711 510637 7511
218 Agora/Proposal/validator/advancing/with 5 cosigners and 1 effects/illegal/invalid governor output datum/authority 15875303 48540 3862
219 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from Draft to VotingReady/proposal 725279200 2049739 13390
220 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from Draft to VotingReady/stake 582713892 1530532 9159
221 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from VotingReady to Locked/proposal 413136164 1128470 10985
222 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from VotingReady to Locked/stake 144761110 375734 6752
223 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from Locked to Finished/proposal 459342271 1258479 12138
224 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from Locked to Finished/stake 201040711 510637 7905
225 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from Locked to Finished/governor 521977563 1334719 11864
226 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to next state/from Locked to Finished/authority 15875303 48540 4256
227 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to failed state/from Draft to Finished/proposal 360413577 996985 10978
228 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to failed state/from Draft to Finished/stake 144761110 375734 6747
229 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to failed state/from VotingReady to Finished/proposal 397057814 1083747 10979
230 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to failed state/from VotingReady to Finished/stake 144761110 375734 6748
231 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to failed state/from Locked to Finished/proposal 399319770 1088555 10979
232 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/legal/to failed state/from Locked to Finished/stake 144761110 375734 6748
233 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake 582713892 1530532 9159
234 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake 144761110 375734 6752
235 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake 201040711 510637 7905
236 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/authority 15875303 48540 4256
237 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/insufficient cosigns/stake 568599606 1489522 9159
238 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/insufficient votes/stake 144761110 375734 6748
239 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/ambiguous winning effect/stake 144761110 375734 6760
240 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/to next state too late/from Draft/stake 582713892 1530532 9161
241 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/to next state too late/from VotingReady/stake 144761110 375734 6752
242 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/to next state too late/from Locked/stake 201040711 510637 7905
243 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/to next state too late/from Locked/governor 521977563 1334719 11864
244 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/to next state too late/from Locked/authority 15875303 48540 4256
245 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/altered output stake datum/from Locked/governor 521977563 1334719 11865
246 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/altered output stake datum/from Locked/authority 15875303 48540 4257
247 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/forget to mint GATs/proposal 435091225 1185870 11426
248 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/forget to mint GATs/stake 187940552 485026 7193
249 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/mint GATs for wrong validators/proposal 467425953 1282682 12365
250 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/mint GATs for wrong validators/stake 204588820 517864 8132
251 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/mint GATs for wrong validators/authority 17699300 53602 4483
252 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/mint GATs with bad token name/proposal 459342271 1258479 11959
253 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/mint GATs with bad token name/stake 201040711 510637 7726
254 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/wrong GAT datum/proposal 459342271 1258479 12132
255 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/wrong GAT datum/stake 201040711 510637 7899
256 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/wrong GAT datum/authority 15875303 48540 4250
257 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/invalid governor output datum/proposal 459342271 1258479 12138
258 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/invalid governor output datum/stake 201040711 510637 7905
259 Agora/Proposal/validator/advancing/with 5 cosigners and 2 effects/illegal/invalid governor output datum/authority 15875303 48540 4256
260 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from Draft to VotingReady/proposal 867130321 2416543 15160
261 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from Draft to VotingReady/stake 582713892 1530532 10339
262 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from VotingReady to Locked/proposal 566071943 1525238 12756
263 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from VotingReady to Locked/stake 144761110 375734 7933
264 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from Locked to Finished/proposal 601193392 1625283 13908
265 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from Locked to Finished/stake 201040711 510637 9085
266 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from Locked to Finished/governor 583614435 1502230 13044
267 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to next state/from Locked to Finished/authority 15875303 48540 5436
268 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to failed state/from Draft to Finished/proposal 502264698 1363789 12749
269 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to failed state/from Draft to Finished/stake 144761110 375734 7928
270 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to failed state/from VotingReady to Finished/proposal 538908935 1450551 12750
271 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to failed state/from VotingReady to Finished/stake 144761110 375734 7929
272 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to failed state/from Locked to Finished/proposal 541170891 1455359 12750
273 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/legal/to failed state/from Locked to Finished/stake 144761110 375734 7929
274 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake 582713892 1530532 10339
275 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake 144761110 375734 7933
276 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake 201040711 510637 9085
277 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/authority 15875303 48540 5436
278 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/insufficient cosigns/stake 568599606 1489522 10339
279 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/insufficient votes/stake 144761110 375734 7929
280 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/ambiguous winning effect/stake 144761110 375734 7953
281 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/to next state too late/from Draft/stake 582713892 1530532 10341
282 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/to next state too late/from VotingReady/stake 144761110 375734 7933
283 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/to next state too late/from Locked/stake 201040711 510637 9085
284 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/to next state too late/from Locked/governor 583614435 1502230 13044
285 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/to next state too late/from Locked/authority 15875303 48540 5436
286 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/altered output stake datum/from Locked/governor 583614435 1502230 13045
287 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/altered output stake datum/from Locked/authority 15875303 48540 5437
288 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/forget to mint GATs/proposal 576942346 1552674 13197
289 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/forget to mint GATs/stake 187940552 485026 8374
290 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/mint GATs for wrong validators/proposal 609277074 1649486 14135
291 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/mint GATs for wrong validators/stake 204588820 517864 9312
292 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/mint GATs for wrong validators/authority 17699300 53602 5663
293 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/mint GATs with bad token name/proposal 601193392 1625283 13730
294 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/mint GATs with bad token name/stake 201040711 510637 8907
295 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/wrong GAT datum/proposal 601193392 1625283 13902
296 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/wrong GAT datum/stake 201040711 510637 9079
297 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/wrong GAT datum/authority 15875303 48540 5430
298 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/invalid governor output datum/proposal 601193392 1625283 13908
299 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/invalid governor output datum/stake 201040711 510637 9085
300 Agora/Proposal/validator/advancing/with 5 cosigners and 5 effects/illegal/invalid governor output datum/authority 15875303 48540 5436
301 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from Draft to VotingReady/proposal 1245892718 3569074 16268
302 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from Draft to VotingReady/stake 1243243041 3300429 12083
303 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from VotingReady to Locked/proposal 390119256 1073794 10847
304 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from VotingReady to Locked/stake 144761110 375734 6660
305 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from Locked to Finished/proposal 440020249 1213791 11999
306 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from Locked to Finished/stake 201040711 510637 7812
307 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from Locked to Finished/governor 510416874 1304742 11771
308 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to next state/from Locked to Finished/authority 15875303 48540 4163
309 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to failed state/from Draft to Finished/proposal 341091555 952297 10840
310 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to failed state/from Draft to Finished/stake 144761110 375734 6655
311 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to failed state/from VotingReady to Finished/proposal 377735792 1039059 10841
312 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to failed state/from VotingReady to Finished/stake 144761110 375734 6656
313 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to failed state/from Locked to Finished/proposal 379997748 1043867 10841
314 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/legal/to failed state/from Locked to Finished/stake 144761110 375734 6656
315 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake 1243243041 3300429 12083
316 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake 144761110 375734 6660
317 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/stake 201040711 510637 7812
318 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/advance finished proposals/(negative test)/authority 15875303 48540 4163
319 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/insufficient cosigns/stake 1257357327 3341439 12083
320 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/insufficient votes/stake 144761110 375734 6656
321 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/ambiguous winning effect/stake 144761110 375734 6664
322 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/to next state too late/from Draft/stake 1243243041 3300429 12085
323 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/to next state too late/from VotingReady/stake 144761110 375734 6660
324 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/to next state too late/from Locked/stake 201040711 510637 7812
325 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/to next state too late/from Locked/governor 510416874 1304742 11771
326 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/to next state too late/from Locked/authority 15875303 48540 4163
327 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/altered output stake datum/from Locked/governor 510416874 1304742 11772
328 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/altered output stake datum/from Locked/authority 15875303 48540 4164
329 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/forget to mint GATs/proposal 415769203 1141182 11288
330 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/forget to mint GATs/stake 187940552 485026 7101
331 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/mint GATs for wrong validators/proposal 448103931 1237994 12226
332 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/mint GATs for wrong validators/stake 204588820 517864 8039
333 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/mint GATs for wrong validators/authority 17699300 53602 4390
334 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/mint GATs with bad token name/proposal 440020249 1213791 11821
335 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/mint GATs with bad token name/stake 201040711 510637 7634
336 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/wrong GAT datum/proposal 440020249 1213791 11993
337 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/wrong GAT datum/stake 201040711 510637 7806
338 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/wrong GAT datum/authority 15875303 48540 4157
339 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/invalid governor output datum/proposal 440020249 1213791 11999
340 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/invalid governor output datum/stake 201040711 510637 7812
341 Agora/Proposal/validator/advancing/with 10 cosigners and 1 effects/illegal/invalid governor output datum/authority 15875303 48540 4163
342 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from Draft to VotingReady/proposal 1293176425 3691342 16858
343 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from Draft to VotingReady/stake 1243243041 3300429 12477
344 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from VotingReady to Locked/proposal 441097849 1206050 11436
345 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from VotingReady to Locked/stake 144761110 375734 7053
346 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from Locked to Finished/proposal 487303956 1336059 12589
347 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from Locked to Finished/stake 201040711 510637 8206
348 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from Locked to Finished/governor 530962498 1360579 12165
349 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to next state/from Locked to Finished/authority 15875303 48540 4557
350 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to failed state/from Draft to Finished/proposal 388375262 1074565 11429
351 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to failed state/from Draft to Finished/stake 144761110 375734 7048
352 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to failed state/from VotingReady to Finished/proposal 425019499 1161327 11430
353 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to failed state/from VotingReady to Finished/stake 144761110 375734 7049
354 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to failed state/from Locked to Finished/proposal 427281455 1166135 11430
355 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/legal/to failed state/from Locked to Finished/stake 144761110 375734 7049
356 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake 1243243041 3300429 12477
357 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake 144761110 375734 7053
358 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/stake 201040711 510637 8206
359 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/advance finished proposals/(negative test)/authority 15875303 48540 4557
360 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/insufficient cosigns/stake 1257357327 3341439 12477
361 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/insufficient votes/stake 144761110 375734 7049
362 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/ambiguous winning effect/stake 144761110 375734 7061
363 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/to next state too late/from Draft/stake 1243243041 3300429 12479
364 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/to next state too late/from VotingReady/stake 144761110 375734 7053
365 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/to next state too late/from Locked/stake 201040711 510637 8206
366 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/to next state too late/from Locked/governor 530962498 1360579 12165
367 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/to next state too late/from Locked/authority 15875303 48540 4557
368 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/altered output stake datum/from Locked/governor 530962498 1360579 12166
369 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/altered output stake datum/from Locked/authority 15875303 48540 4558
370 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/forget to mint GATs/proposal 463052910 1263450 11877
371 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/forget to mint GATs/stake 187940552 485026 7494
372 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/mint GATs for wrong validators/proposal 495387638 1360262 12816
373 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/mint GATs for wrong validators/stake 204588820 517864 8433
374 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/mint GATs for wrong validators/authority 17699300 53602 4784
375 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/mint GATs with bad token name/proposal 487303956 1336059 12410
376 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/mint GATs with bad token name/stake 201040711 510637 8027
377 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/wrong GAT datum/proposal 487303956 1336059 12583
378 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/wrong GAT datum/stake 201040711 510637 8200
379 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/wrong GAT datum/authority 15875303 48540 4551
380 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/invalid governor output datum/proposal 487303956 1336059 12589
381 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/invalid governor output datum/stake 201040711 510637 8206
382 Agora/Proposal/validator/advancing/with 10 cosigners and 2 effects/illegal/invalid governor output datum/authority 15875303 48540 4557
383 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from Draft to VotingReady/proposal 1435027546 4058146 18629
384 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from Draft to VotingReady/stake 1243243041 3300429 13657
385 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from VotingReady to Locked/proposal 594033628 1602818 13208
386 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from VotingReady to Locked/stake 144761110 375734 8234
387 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from Locked to Finished/proposal 629155077 1702863 14361
388 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from Locked to Finished/stake 201040711 510637 9387
389 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from Locked to Finished/governor 592599370 1528090 13346
390 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to next state/from Locked to Finished/authority 15875303 48540 5738
391 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to failed state/from Draft to Finished/proposal 530226383 1441369 13201
392 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to failed state/from Draft to Finished/stake 144761110 375734 8229
393 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to failed state/from VotingReady to Finished/proposal 566870620 1528131 13202
394 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to failed state/from VotingReady to Finished/stake 144761110 375734 8230
395 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to failed state/from Locked to Finished/proposal 569132576 1532939 13202
396 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/legal/to failed state/from Locked to Finished/stake 144761110 375734 8230
397 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake 1243243041 3300429 13657
398 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake 144761110 375734 8234
399 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/stake 201040711 510637 9387
400 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/advance finished proposals/(negative test)/authority 15875303 48540 5738
401 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/insufficient cosigns/stake 1257357327 3341439 13657
402 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/insufficient votes/stake 144761110 375734 8230
403 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/ambiguous winning effect/stake 144761110 375734 8254
404 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/to next state too late/from Draft/stake 1243243041 3300429 13659
405 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/to next state too late/from VotingReady/stake 144761110 375734 8234
406 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/to next state too late/from Locked/stake 201040711 510637 9387
407 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/to next state too late/from Locked/governor 592599370 1528090 13346
408 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/to next state too late/from Locked/authority 15875303 48540 5738
409 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/altered output stake datum/from Locked/governor 592599370 1528090 13347
410 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/altered output stake datum/from Locked/authority 15875303 48540 5739
411 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/forget to mint GATs/proposal 604904031 1630254 13649
412 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/forget to mint GATs/stake 187940552 485026 8675
413 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/mint GATs for wrong validators/proposal 637238759 1727066 14587
414 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/mint GATs for wrong validators/stake 204588820 517864 9613
415 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/mint GATs for wrong validators/authority 17699300 53602 5964
416 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/mint GATs with bad token name/proposal 629155077 1702863 14182
417 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/mint GATs with bad token name/stake 201040711 510637 9208
418 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/wrong GAT datum/proposal 629155077 1702863 14355
419 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/wrong GAT datum/stake 201040711 510637 9381
420 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/wrong GAT datum/authority 15875303 48540 5731
421 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/invalid governor output datum/proposal 629155077 1702863 14361
422 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/invalid governor output datum/stake 201040711 510637 9387
423 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/invalid governor output datum/authority 15875303 48540 5738
424 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: retract votes while voting/stake 140863709 370195 5783
425 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: retract votes while voting/proposal 282615841 794299 9497
426 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: retract votes while voting/stake 147212927 388303 5799
427 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: retract votes while voting/proposal 300115413 843740 9508
428 Agora/Proposal/validator/unlocking/legal/with 1 proposals/creator: remove creator locks when finished/stake 139825432 367397 5781
429 Agora/Proposal/validator/unlocking/legal/with 1 proposals/creator: remove creator locks when finished/proposal 256687085 727957 9494
430 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove all locks when finished/stake 143981681 379249 5797
431 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove all locks when finished/proposal 264924554 752465 9506
432 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Locked/stake 140863709 370195 5787
433 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Locked/proposal 252614193 719732 9501
434 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Finished/stake 140863709 370195 5787
435 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Finished/proposal 253179682 720934 9501
436 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove vote locks when locked/stake 147212927 388303 5803
437 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove vote locks when locked/proposal 270679254 770375 9512
438 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: retract votes while voting/stake 297243845 781199 7876
439 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: retract votes while voting/proposal 425257089 1206963 11566
440 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: retract votes while voting/stake 328989935 871739 7952
441 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: retract votes while voting/proposal 494812989 1407716 11617
442 Agora/Proposal/validator/unlocking/legal/with 5 proposals/creator: remove creator locks when finished/stake 292052460 767209 7865
443 Agora/Proposal/validator/unlocking/legal/with 5 proposals/creator: remove creator locks when finished/proposal 364767897 1045813 11558
444 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove all locks when finished/stake 312833705 826469 7946
445 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove all locks when finished/proposal 402960090 1157169 11611
446 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Locked/stake 297243845 781199 7896
447 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Locked/proposal 364848113 1048780 11586
448 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Finished/stake 297243845 781199 7896
449 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Finished/proposal 365413602 1049982 11586
450 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove vote locks when locked/stake 328989935 871739 7972
451 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove vote locks when locked/proposal 434969502 1250735 11637
452 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: retract votes while voting/stake 492719015 1294954 10491
453 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: retract votes while voting/proposal 603558649 1722793 14151
454 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: retract votes while voting/stake 556211195 1476034 10642
455 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: retract votes while voting/proposal 738184959 2112686 14252
456 Agora/Proposal/validator/unlocking/legal/with 10 proposals/creator: remove creator locks when finished/stake 482336245 1266974 10471
457 Agora/Proposal/validator/unlocking/legal/with 10 proposals/creator: remove creator locks when finished/proposal 499868912 1443133 14139
458 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove all locks when finished/stake 523898735 1385494 10631
459 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove all locks when finished/proposal 575504510 1663049 14241
460 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Locked/stake 492719015 1294954 10531
461 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Locked/proposal 505140513 1460090 14191
462 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Finished/stake 492719015 1294954 10531
463 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Finished/proposal 505706002 1461292 14191
464 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove vote locks when locked/stake 556211195 1476034 10682
465 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove vote locks when locked/proposal 640332312 1851185 14292
466 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: retract votes while voting/stake 1743760103 4582986 27321
467 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: retract votes while voting/proposal 1744688633 5024105 30770
468 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: retract votes while voting/stake 2010427259 5343522 28008
469 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: retract votes while voting/proposal 2295765567 6624494 31228
470 Agora/Proposal/validator/unlocking/legal/with 42 proposals/creator: remove creator locks when finished/stake 1700152469 4465470 27236
471 Agora/Proposal/validator/unlocking/legal/with 42 proposals/creator: remove creator locks when finished/proposal 1364515408 3985981 30725
472 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove all locks when finished/stake 1874714927 4963254 27947
473 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove all locks when finished/proposal 1679788798 4900681 31167
474 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Locked/stake 1743760103 4582986 27489
475 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Locked/proposal 1403011873 4092474 30938
476 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Finished/stake 1743760103 4582986 27489
477 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Finished/proposal 1403577362 4093676 30938
478 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove vote locks when locked/stake 2010427259 5343522 28177
479 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove vote locks when locked/proposal 1954654296 5694065 31397
480 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter , status: Draft/stake 140863709 370195 5783
481 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter , status: Locked/stake 140863709 370195 5783
482 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter , status: Finished/stake 140863709 370195 5783
483 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator , status: Draft/stake 135454846 355547 5785
484 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator , status: Locked/stake 135454846 355547 5785
485 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator , status: Finished/stake 135454846 355547 5785
486 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both , status: Draft/stake 147212927 388303 5799
487 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both , status: Locked/stake 147212927 388303 5799
488 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both , status: Finished/stake 147212927 388303 5799
489 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake 129105628 337439 5765
490 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake 129105628 337439 5765
491 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake 129105628 337439 5765
492 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake 129105628 337439 5765
493 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake 129105628 337439 5765
494 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake 129105628 337439 5765
495 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake 129105628 337439 5765
496 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake 129105628 337439 5765
497 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake 129105628 337439 5765
498 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake 129105628 337439 5765
499 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake 129105628 337439 5765
500 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: Draft/stake 139825432 367397 5781
501 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: VotingReady/stake 139825432 367397 5781
502 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: Locked/stake 139825432 367397 5781
503 Agora/Proposal/validator/unlocking/illegal/with 1 proposals/creator: retract votes/stake 139825432 367397 5779
504 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter , status: Draft/stake 297243845 781199 7876
505 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter , status: Locked/stake 297243845 781199 7876
506 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter , status: Finished/stake 297243845 781199 7876
507 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator , status: Draft/stake 285403194 749767 7882
508 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator , status: Locked/stake 285403194 749767 7882
509 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator , status: Finished/stake 285403194 749767 7882
510 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both , status: Draft/stake 328989935 871739 7952
511 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both , status: Locked/stake 328989935 871739 7952
512 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both , status: Finished/stake 328989935 871739 7952
513 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake 253657104 659227 7793
514 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake 253657104 659227 7793
515 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake 253657104 659227 7793
516 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake 253657104 659227 7793
517 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake 253657104 659227 7793
518 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake 253657104 659227 7793
519 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake 253657104 659227 7793
520 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake 253657104 659227 7793
521 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake 253657104 659227 7793
522 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake 253657104 659227 7793
523 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake 253657104 659227 7793
524 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: Draft/stake 292052460 767209 7865
525 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: VotingReady/stake 292052460 767209 7865
526 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: Locked/stake 292052460 767209 7865
527 Agora/Proposal/validator/unlocking/illegal/with 5 proposals/creator: retract votes/stake 292052460 767209 7855
528 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter , status: Draft/stake 492719015 1294954 10491
529 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter , status: Locked/stake 492719015 1294954 10491
530 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter , status: Finished/stake 492719015 1294954 10491
531 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator , status: Draft/stake 472838629 1242542 10502
532 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator , status: Locked/stake 472838629 1242542 10502
533 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator , status: Finished/stake 472838629 1242542 10502
534 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both , status: Draft/stake 556211195 1476034 10642
535 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both , status: Locked/stake 556211195 1476034 10642
536 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both , status: Finished/stake 556211195 1476034 10642
537 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake 409346449 1061462 10328
538 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake 409346449 1061462 10328
539 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake 409346449 1061462 10328
540 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake 409346449 1061462 10328
541 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake 409346449 1061462 10328
542 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake 409346449 1061462 10328
543 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake 409346449 1061462 10328
544 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake 409346449 1061462 10328
545 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake 409346449 1061462 10328
546 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake 409346449 1061462 10328
547 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake 409346449 1061462 10328
548 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: Draft/stake 482336245 1266974 10471
549 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: VotingReady/stake 482336245 1266974 10471
550 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: Locked/stake 482336245 1266974 10471
551 Agora/Proposal/validator/unlocking/illegal/with 10 proposals/creator: retract votes/stake 482336245 1266974 10450
552 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter , status: Draft/stake 1743760103 4582986 27321
553 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter , status: Locked/stake 1743760103 4582986 27321
554 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter , status: Finished/stake 1743760103 4582986 27321
555 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator , status: Draft/stake 1672425413 4396302 27382
556 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator , status: Locked/stake 1672425413 4396302 27382
557 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator , status: Finished/stake 1672425413 4396302 27382
558 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both , status: Draft/stake 2010427259 5343522 28008
559 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both , status: Locked/stake 2010427259 5343522 28008
560 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both , status: Finished/stake 2010427259 5343522 28008
561 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake 1405758257 3635766 26608
562 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake 1405758257 3635766 26608
563 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake 1405758257 3635766 26608
564 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake 1405758257 3635766 26608
565 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake 1405758257 3635766 26608
566 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake 1405758257 3635766 26608
567 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake 1405758257 3635766 26608
568 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake 1405758257 3635766 26608
569 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake 1405758257 3635766 26608
570 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake 1405758257 3635766 26608
571 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake 1405758257 3635766 26608
572 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: Draft/stake 1700152469 4465470 27236
573 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: VotingReady/stake 1700152469 4465470 27236
574 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: Locked/stake 1700152469 4465470 27236
575 Agora/Proposal/validator/unlocking/illegal/with 42 proposals/creator: retract votes/stake 1700152469 4465470 27152
576 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple 19822997 52452 427
577 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs 32009395 84810 527
578 Agora/Treasury/Validator/Positive/Allows for effect changes 31553082 81982 1423
579 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple 19822997 52452 427
580 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs 32009395 84810 527
581 Agora/Governor/policy/totally legal 70301827 197578 2576
582 Agora/Governor/validator/mutate/legal 119848510 323653 9297

View file

@ -3,7 +3,4 @@ packages: ./.
benchmarks: true benchmarks: true
tests: true tests: true
package plutarch test-show-details: direct
flags: +development
test-show-details: direct

8526
flake.lock generated

File diff suppressed because it is too large Load diff

334
flake.nix
View file

@ -1,253 +1,109 @@
{ {
description = "agora"; description = "agora";
inputs.nixpkgs.follows = "plutarch/nixpkgs"; inputs = {
inputs.haskell-nix.follows = "plutarch/haskell-nix"; nixpkgs.follows = "plutarch/nixpkgs";
inputs.nixpkgs-latest.url = "github:NixOS/nixpkgs?rev=a0a69be4b5ee63f1b5e75887a406e9194012b492"; nixpkgs-latest.url = "github:NixOS/nixpkgs?rev=cf63df0364f67848083ff75bc8ac9b7ca7aa5a01";
# temporary fix for nix versions that have the transitive follows bug # temporary fix for nix versions that have the transitive follows bug
# see https://github.com/NixOS/nix/issues/6013 # see https://github.com/NixOS/nix/issues/6013
inputs.nixpkgs-2111 = { url = "github:NixOS/nixpkgs/nixpkgs-21.11-darwin"; }; nixpkgs-2111 = { url = "github:NixOS/nixpkgs/nixpkgs-21.11-darwin"; };
# Plutarch and its friends haskell-nix-extra-hackage.follows = "plutarch/haskell-nix-extra-hackage";
inputs.plutarch.url = haskell-nix.follows = "plutarch/haskell-nix";
"github:liqwid-labs/plutarch?rev=e7ef565645146e26e75ec29fe97122a74e52c6b7"; iohk-nix.follows = "plutarch/iohk-nix";
inputs.plutarch.inputs.emanote.follows = haskell-language-server.follows = "plutarch/haskell-language-server";
"plutarch/haskell-nix/nixpkgs-unstable";
inputs.plutarch.inputs.nixpkgs.follows =
"plutarch/haskell-nix/nixpkgs-unstable";
inputs.liqwid-plutarch-extra.url = # Plutarch and its friends
"github:Liqwid-Labs/liqwid-plutarch-extra?ref=main"; plutarch = {
inputs.plutarch-numeric.url = url = "github:Plutonomicon/plutarch-plutus?ref=staging";
"github:Liqwid-Labs/plutarch-numeric?ref=main";
inputs.plutarch-safe-money.url =
"github:Liqwid-Labs/plutarch-safe-money?rev=9f968b80189c7e4b335527cd5b103dc26952f667";
# Testing inputs.emanote.follows =
inputs.plutarch-quickcheck.url = "plutarch/haskell-nix/nixpkgs-unstable";
"github:liqwid-labs/plutarch-quickcheck?ref=staging"; inputs.nixpkgs.follows =
inputs.plutarch-context-builder.url = "plutarch/haskell-nix/nixpkgs-unstable";
"github:Liqwid-Labs/plutarch-context-builder?ref=staging"; };
outputs = inputs@{ self, nixpkgs, nixpkgs-latest, haskell-nix, plutarch, ... }: plutarch-numeric.url =
"github:Liqwid-Labs/plutarch-numeric?ref=main";
plutarch-safe-money.url =
"github:Liqwid-Labs/plutarch-safe-money?ref=main";
liqwid-plutarch-extra.url =
"github:Liqwid-Labs/liqwid-plutarch-extra?ref=plutus-v1";
plutarch-quickcheck.url =
"github:liqwid-labs/plutarch-quickcheck?ref=staging";
plutarch-context-builder.url =
"github:Liqwid-Labs/plutarch-context-builder?ref=plutus-v1";
plutarch-script-export.url =
"github:Liqwid-Labs/plutarch-script-export?ref=main";
liqwid-nix.url = "github:Liqwid-Labs/liqwid-nix?ref=main";
};
outputs = inputs@{ liqwid-nix, ... }:
let let
supportedSystems = nixpkgs-latest.lib.systems.flakeExposed; benchCheckOverlay = self: super: {
toFlake =
perSystem = nixpkgs.lib.genAttrs supportedSystems; let
inherit (self) inputs perSystem pkgsFor';
pkgsFor = system: import nixpkgs { flake = super.toFlake or { };
inherit system; name = "benchCheck";
overlays = [ haskell-nix.overlay (import "${plutarch.inputs.iohk-nix}/overlays/crypto") ]; in
# This only does bad things for us... flake // {
# inherit (haskell-nix) config; checks = perSystem (system:
}; flake.checks.${system} // {
pkgsFor' = system: import nixpkgs-latest { inherit system; }; ${name} =
let
fourmoluFor = system: (pkgsFor' system).haskell.packages.ghc922.fourmolu_0_6_0_0; pkgs' = pkgsFor' system;
bench = flake.packages.${system}."agora:bench:agora-bench";
defaultGhcVersion = "ghc923"; in
pkgs'.runCommand name
nonReinstallablePkgs = [ {
"array" nativeBuildInputs = [ pkgs'.diffutils ];
"array" } ''
"base" export LC_CTYPE=C.UTF-8
"binary" export LC_ALL=C.UTF-8
"bytestring" export LANG=C.UTF-8
"Cabal" cd ${inputs.self}
"containers" ${bench}/bin/agora-bench | diff bench.csv - \
"deepseq" || (echo "bench.csv is outdated"; exit 1)
"directory" mkdir "$out"
"exceptions" '';
"filepath" });
"ghc"
"ghc-bignum"
"ghc-boot"
"ghc-boot"
"ghc-boot-th"
"ghc-compact"
"ghc-heap"
# "ghci"
# "haskeline"
"ghcjs-prim"
"ghcjs-th"
"ghc-prim"
"ghc-prim"
"hpc"
"integer-gmp"
"integer-simple"
"mtl"
"parsec"
"pretty"
"process"
"rts"
"stm"
"template-haskell"
"terminfo"
"text"
"time"
"transformers"
"unix"
"Win32"
"xhtml"
];
haskellModules = [
({ config, pkgs, hsPkgs, ... }: {
inherit nonReinstallablePkgs; # Needed for a lot of different things
packages = {
cardano-binary.doHaddock = false;
cardano-binary.ghcOptions = [ "-Wwarn" ];
cardano-crypto-class.components.library.pkgconfig = pkgs.lib.mkForce [ [ pkgs.libsodium-vrf ] ];
cardano-crypto-class.doHaddock = false;
cardano-crypto-class.ghcOptions = [ "-Wwarn" ];
cardano-crypto-praos.components.library.pkgconfig = pkgs.lib.mkForce [ [ pkgs.libsodium-vrf ] ];
cardano-prelude.doHaddock = false; # somehow above options are not applied?
cardano-prelude.ghcOptions = [ "-Wwarn" ];
# Workaround missing support for build-tools:
# https://github.com/input-output-hk/haskell.nix/issues/231
plutarch-test.components.exes.plutarch-test.build-tools = [
config.hsPkgs.hspec-discover
];
}; };
}) };
]; in
(liqwid-nix.buildProject
myhackage = system: compiler-nix-name: plutarch.inputs.haskell-nix-extra-hackage.mkHackageFor system compiler-nix-name ( {
[ inherit inputs;
"${inputs.plutarch.inputs.flat}" src = ./.;
"${inputs.plutarch.inputs.protolude}" }
"${inputs.plutarch.inputs.cardano-prelude}/cardano-prelude" [
"${inputs.plutarch.inputs.cardano-crypto}" liqwid-nix.haskellProject
"${inputs.plutarch.inputs.cardano-base}/binary" liqwid-nix.plutarchProject
"${inputs.plutarch.inputs.cardano-base}/cardano-crypto-class" (liqwid-nix.addDependencies [
"${inputs.plutarch.inputs.plutus}/plutus-core"
"${inputs.plutarch.inputs.plutus}/plutus-ledger-api"
"${inputs.plutarch.inputs.plutus}/plutus-tx"
"${inputs.plutarch.inputs.plutus}/prettyprinter-configurable"
"${inputs.plutarch.inputs.plutus}/word-array"
"${inputs.plutarch.inputs.secp256k1-haskell}"
"${inputs.plutarch.inputs.plutus}/plutus-tx-plugin" # necessary for FFI tests
# Custom deps as a consumer
"${inputs.plutarch}"
"${inputs.plutarch}/plutarch-extra"
"${inputs.liqwid-plutarch-extra}"
"${inputs.plutarch-numeric}" "${inputs.plutarch-numeric}"
"${inputs.plutarch-safe-money}" "${inputs.plutarch-safe-money}"
"${inputs.plutarch-quickcheck}" "${inputs.plutarch-quickcheck}"
"${inputs.plutarch-context-builder}" "${inputs.plutarch-context-builder}"
] "${inputs.liqwid-plutarch-extra}"
); "${inputs.plutarch-script-export}"
])
applyDep = pkgs: o: (liqwid-nix.enableFormatCheck [
let h = myhackage pkgs.system o.compiler-nix-name; in "-XQuasiQuotes"
(plutarch.applyPlutarchDep pkgs o) // { "-XTemplateHaskell"
modules = haskellModules ++ [ h.module ] ++ (o.modules or [ ]); "-XTypeApplications"
extra-hackages = [ (import h.hackageNix) ] ++ (o.extra-hackages or [ ]); "-XImportQualifiedPost"
extra-hackage-tarballs = { _xNJUd_plutarch-hackage = h.hackageTarball; } // (o.extra-hackage-tarballs or { }); "-XPatternSynonyms"
}; "-XOverloadedRecordDot"
])
projectForGhc = compiler-nix-name: system: liqwid-nix.enableLintCheck
let pkgs = pkgsFor system; in liqwid-nix.enableCabalFormatCheck
let pkgs' = pkgsFor' system; in liqwid-nix.enableNixFormatCheck
let pkgSet = pkgs.haskell-nix.cabalProject' (applyDep pkgs { liqwid-nix.addBuildChecks
src = ./.; (liqwid-nix.addCommandLineTools (pkgs: _: [
inherit compiler-nix-name; pkgs.haskellPackages.hasktags
modules = [ ]; ]))
shell = { benchCheckOverlay
withHoogle = true; ]
).toFlake;
exactDeps = true;
# We use the ones from Nixpkgs, since they are cached reliably.
# Eventually we will probably want to build these with haskell.nix.
nativeBuildInputs = [
pkgs'.cabal-install
pkgs'.hlint
pkgs'.haskellPackages.cabal-fmt
(fourmoluFor system)
pkgs'.nixpkgs-fmt
(plutarch.hlsFor compiler-nix-name system)
];
};
}); in
pkgSet;
projectFor = projectForGhc defaultGhcVersion;
formatCheckFor = system:
let
pkgs' = pkgsFor' system;
in
pkgs'.runCommand "format-check"
{
nativeBuildInputs = [ pkgs'.haskellPackages.cabal-fmt pkgs'.nixpkgs-fmt (fourmoluFor system) pkgs'.hlint ];
} ''
export LC_CTYPE=C.UTF-8
export LC_ALL=C.UTF-8
export LANG=C.UTF-8
cd ${self}
make format_check || (echo " Please run 'make format'" ; exit 1)
find -name '*.hs' -not -path './dist*/*' -not -path './haddock/*' | xargs hlint
mkdir $out
''
;
benchCheckFor = system: agora-bench:
let
pkgs = pkgsFor system;
pkgs' = pkgsFor' system;
in
pkgs.runCommand "bench-check"
{
bench = "${agora-bench}/bin/agora-bench";
nativeBuildInputs = [
pkgs'.diffutils
];
} ''
export LC_CTYPE=C.UTF-8
export LC_ALL=C.UTF-8
export LANG=C.UTF-8
cd ${self}
make bench_check || (echo " Please run 'make bench'" ; exit 1)
mkdir $out
'';
in
{
project = perSystem projectFor;
flake = perSystem (system: (projectFor system).flake { });
packages = perSystem (system:
self.flake.${system}.packages // {
haddock =
let
agora-doc = self.flake.${system}.packages."agora:lib:agora".doc;
pkgs = pkgsFor system;
in
pkgs.runCommand "haddock-merge" { } ''
cd ${self}
mkdir $out
cp -r ${agora-doc}/share/doc/* $out
'';
});
# Define what we want to test
checks = perSystem (system:
self.flake.${system}.checks // {
formatCheck = formatCheckFor system;
# benchCheck = benchCheckFor system self.flake.${system}.packages."agora:bench:agora-bench";
agora = self.flake.${system}.packages."agora:lib:agora";
agora-test = self.flake.${system}.packages."agora:test:agora-test";
benchCheck = benchCheckFor system self.flake.${system}.packages."agora:bench:agora-bench";
});
check = perSystem (system:
(pkgsFor system).runCommand "combined-test"
{
checksss = builtins.attrValues self.checks.${system};
} ''
echo $checksss
touch $out
'');
devShell = perSystem (system: self.flake.${system}.devShell);
};
} }