commit
4eb5fe2dee
66 changed files with 13137 additions and 7997 deletions
78
.github/workflows/integrate.yaml
vendored
78
.github/workflows/integrate.yaml
vendored
|
|
@ -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
|
|
||||||
64
CHANGELOG.md
64
CHANGELOG.md
|
|
@ -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
|
||||||
|
|
|
||||||
99
Makefile
99
Makefile
|
|
@ -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
|
||||||
|
|
|
||||||
21
README.md
21
README.md
|
|
@ -1,5 +1,4 @@
|
||||||
# Agora :classical_building:
|
# Agora :classical_building:
|
||||||

|
|
||||||
|
|
||||||
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.
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
164
agora-scripts/Main.hs
Normal 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
|
||||||
|
|
@ -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."
|
|
||||||
)
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"governorInitialSpend": {
|
|
||||||
"txOutRefId": "7be688c61c209dd7a9f4948090db0b031b11a8850b0ee4695f786fea85fbfdee",
|
|
||||||
"txOutRefIdx": 0
|
|
||||||
},
|
|
||||||
"gtClassRef": [
|
|
||||||
"",
|
|
||||||
""
|
|
||||||
],
|
|
||||||
"maximumCosigners": 5
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
]
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
279
agora-specs/Sample/Governor/Initialize.hs
Normal file
279
agora-specs/Sample/Governor/Initialize.hs
Normal 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)
|
||||||
269
agora-specs/Sample/Governor/Mutate.hs
Normal file
269
agora-specs/Sample/Governor/Mutate.hs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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]
|
|
||||||
}
|
|
||||||
1083
agora-specs/Sample/Proposal/Advance.hs
Normal file
1083
agora-specs/Sample/Proposal/Advance.hs
Normal file
File diff suppressed because it is too large
Load diff
339
agora-specs/Sample/Proposal/Cosign.hs
Normal file
339
agora-specs/Sample/Proposal/Cosign.hs
Normal 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)
|
||||||
458
agora-specs/Sample/Proposal/Create.hs
Normal file
458
agora-specs/Sample/Proposal/Create.hs
Normal 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)
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
292
agora-specs/Sample/Proposal/Vote.hs
Normal file
292
agora-specs/Sample/Proposal/Vote.hs
Normal 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)
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
209
agora-specs/Sample/Stake/SetDelegate.hs
Normal file
209
agora-specs/Sample/Stake/SetDelegate.hs
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
43
agora.cabal
43
agora.cabal
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
67
agora/Agora/Bootstrap.hs
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 ())
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
138
agora/Agora/Scripts.hs
Normal 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
|
||||||
|
|
@ -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)
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 ()
|
||||||
|
|
|
||||||
|
|
@ -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
617
bench.csv
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
|
@ -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
8526
flake.lock
generated
File diff suppressed because it is too large
Load diff
334
flake.nix
334
flake.nix
|
|
@ -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);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue