packages feed

cuddle-0.5.0.0: example/Conway.hs

{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude #-}

{- HLINT ignore "Use camelCase" -}
{- HLINT ignore "Evaluate" -}

module Conway where

import Codec.CBOR.Cuddle.Huddle
import Data.Function (($))
import Data.Semigroup ((<>))
import Data.Text qualified as T
import Data.Word (Word64)
import GHC.Float (Double)
import GHC.Num (Integer)
import GHC.Show (Show (show))

default (Integer, Double)

conway :: Huddle
conway = collectFrom [HIRule block]

block :: Rule
block =
  "block"
    =:= arr
      [ a header
      , "transaction_bodies" ==> arr [0 <+ a transaction_body]
      , "transaction_witness_sets"
          ==> arr [0 <+ a transaction_witness_set]
      , "auxiliary_data_set"
          ==> mp [0 <+ asKey transaction_index ==> auxiliary_data]
      , "invalid_transactions" ==> arr [0 <+ a transaction_index]
      ]

transaction :: Rule
transaction =
  "transaction"
    =:= arr
      [ a transaction_body
      , a transaction_witness_set
      , a VBool
      , a (auxiliary_data / VNil)
      ]

transaction_index :: Rule
transaction_index = "transaction_index" =:= VUInt `sized` (2 :: Word64)

header :: Rule
header = "header" =:= arr [a header_body, "body_signature" ==> kes_signature]

header_body :: Rule
header_body =
  "header_body"
    =:= arr
      [ "block_number" ==> VUInt
      , "slot" ==> VUInt
      , "prev_hash" ==> hash32
      , "issuer_vkey" ==> vkey
      , "vrf_vkey" ==> vkey
      , "vrf_result" ==> vrf_cert
      , "block_body_size" ==> VUInt
      , "block_body_hash" ==> hash32
      , a operational_cert
      , a (arr [a protocol_version])
      ]

operational_cert :: Rule
operational_cert =
  "operational_cert"
    =:= arr
      [ "hot_vkey" ==> kes_vkey
      , "sequence_number" ==> VUInt
      , "kes_period" ==> VUInt
      , "sigma" ==> signature
      ]

protocol_version :: Named Group
protocol_version = "protocol_version" =:~ grp [a major_protocol_version, a VUInt]

next_major_protocol_version :: Rule
next_major_protocol_version = "next_major_protocol_version" =:= (10 :: Integer)

major_protocol_version :: Rule
major_protocol_version = "major_protocol_version" =:= (1 :: Integer)

transaction_body :: Rule
transaction_body =
  "transaction_body"
    =:= mp
      [ idx 0 ==> set transaction_index
      , idx 1 ==> arr [0 <+ a transaction_output]
      , idx 2 ==> coin
      , opt (idx 3 ==> VUInt)
      , opt (idx 4 ==> certificates)
      , opt (idx 5 ==> withdrawals)
      , opt (idx 7 ==> auxiliary_data_hash)
      , opt (idx 8 ==> VUInt)
      , opt (idx 9 ==> mint)
      , opt (idx 11 ==> script_data_hash)
      , opt (idx 13 ==> nonempty_set transaction_input)
      , opt (idx 14 ==> required_signers)
      , opt (idx 15 ==> network_id)
      , opt (idx 16 ==> transaction_output)
      , opt (idx 17 ==> coin)
      , opt (idx 18 ==> nonempty_set transaction_input)
      , opt (idx 19 ==> voting_procedures)
      , opt (idx 20 ==> proposal_procedures)
      , opt (idx 21 ==> coin)
      , opt (idx 22 ==> positive_coin)
      ]

voting_procedures :: Rule
voting_procedures =
  "voting_procedures"
    =:= mp [1 <+ "voter" ==> mp [1 <+ "gov_action_id" ==> voting_procedure]]

voting_procedure :: Rule
voting_procedure = "voting_procedure" =:= arr [a vote, a (anchor / VNil)]

proposal_procedure :: Rule
proposal_procedure =
  "proposal_procedure"
    =:= arr
      [ "deposit" ==> coin
      , a reward_account
      , a gov_action
      , a anchor
      ]

proposal_procedures :: Rule
proposal_procedures = "proposal_procedures" =:= nonempty_set proposal_procedure

certificates :: Rule
certificates = "certificates" =:= nonempty_set certificate

gov_action :: Rule
gov_action =
  "gov_action"
    =:= arr
      [ a
          ( parameter_change_action
              / hard_fork_initiation_action
              / treasury_withdrawals_action
              / no_confidence
              / update_committee
              / new_constitution
              / info_action
          )
      ]

policy_hash :: Rule
policy_hash = "policy_hash" =:= scripthash

parameter_change_action :: Named Group
parameter_change_action =
  "parameter_change_action"
    =:~ grp
      [ 0
      , a (gov_action_id / VNil)
      , a protocol_param_update
      , a (policy_hash / VNil)
      ]

hard_fork_initiation_action :: Named Group
hard_fork_initiation_action =
  "hard_fork_initiation_action"
    =:~ grp [1, a (gov_action_id / VNil), a (arr [a protocol_version])]

treasury_withdrawals_action :: Named Group
treasury_withdrawals_action =
  "treasury_withdrawals_action"
    =:~ grp [2, a (arr [asKey reward_account ==> coin / VInt]), a (policy_hash / VNil)]

no_confidence :: Named Group
no_confidence = "no_confidence" =:~ grp [3, a (gov_action_id / VNil)]

update_committee :: Named Group
update_committee =
  "update_committee"
    =:~ grp
      [ 4
      , a (gov_action_id / VNil)
      , a (set committee_cold_credential)
      , a (arr [asKey committee_cold_credential ==> epoch])
      , a unit_interval
      ]

new_constitution :: Named Group
new_constitution =
  "new_constitution"
    =:~ grp [5, a (gov_action_id / VNil), a constitution]

constitution :: Rule
constitution =
  "constitution"
    =:= arr
      [ a anchor
      , a (scripthash / VNil)
      ]

info_action :: Rule
info_action = "info_action" =:= int 6

voter :: Rule
voter =
  "voter"
    =:= arr [0, a addr_keyhash]
    / arr [1, a scripthash]
    / arr [2, a addr_keyhash]
    / arr [3, a scripthash]
    / arr [4, a addr_keyhash]

anchor :: Rule
anchor =
  "anchor"
    =:= arr
      [ "anchor_url" ==> url
      , "anchor_data_hash" ==> hash32
      ]

vote :: Rule
vote = "vote" =:= int 0 / int 1 / int 2

gov_action_id :: Rule
gov_action_id =
  "gov_action_id"
    =:= arr
      [ "transaction_id" ==> hash32
      , "gov_action_index" ==> VUInt
      ]

required_signers :: Rule
required_signers = "required_signers" =:= nonempty_set addr_keyhash

transaction_input :: Rule
transaction_input =
  "transaction_input"
    =:= arr
      [ "transaction_id" ==> hash32
      , "index" ==> VUInt
      ]

transaction_output :: Rule
transaction_output =
  "transaction_output"
    =:= legacy_transaction_output
    / post_alonzo_transaction_output

legacy_transaction_output :: Rule
legacy_transaction_output =
  "legacy_transaction_output"
    =:= arr
      [ a address
      , "amount" ==> value
      , opt ("datum_hash" ==> hash32)
      ]

post_alonzo_transaction_output :: Rule
post_alonzo_transaction_output =
  "post_alonzo_transaction_output"
    =:= arr
      [ idx 0 ==> address
      , idx 1 ==> value
      , opt (idx 2 ==> datum_option) -- datum option
      , opt (idx 3 ==> script_ref) -- script reference
      ]

script_data_hash :: Rule
script_data_hash = "script_data_hash" =:= hash32

certificate :: Rule
certificate =
  "certificate"
    =:= arr
      [ a
          ( stake_registration
              / stake_deregistration
              / stake_delegation
              / pool_registration
              / pool_retirement
              / reg_cert
              / unreg_cert
              / vote_deleg_cert
              / stake_vote_deleg_cert
              / stake_reg_deleg_cert
              / vote_reg_deleg_cert
              / stake_vote_reg_deleg_cert
              / auth_committee_hot_cert
              / resign_committee_cold_cert
              / reg_drep_cert
              / unreg_drep_cert
              / update_drep_cert
          )
      ]

stake_registration :: Named Group
stake_registration = "stake_registration" =:~ grp [0, a stake_credential]

stake_deregistration :: Named Group
stake_deregistration = "stake_deregistration" =:~ grp [0, a stake_credential]

stake_delegation :: Named Group
stake_delegation =
  "stake_delegation"
    =:~ grp [0, a stake_credential, a pool_keyhash]

-- POOL
pool_registration :: Named Group
pool_registration = "pool_registration" =:~ grp [3, a pool_params]

pool_retirement :: Named Group
pool_retirement = "pool_retirement" =:~ grp [4, a pool_keyhash, a epoch]

-- numbers 5 and 6 used to be the Genesis and MIR certificates respectively,
-- which were deprecated in Conway

-- DELEG
reg_cert :: Named Group
reg_cert = "reg_cert" =:~ grp [7, a stake_credential, a coin]

unreg_cert :: Named Group
unreg_cert = "unreg_cert" =:~ grp [8, a stake_credential, a coin]

vote_deleg_cert :: Named Group
vote_deleg_cert = "vote_deleg_cert" =:~ grp [9, a stake_credential, a drep]

stake_vote_deleg_cert :: Named Group
stake_vote_deleg_cert =
  "stake_vote_deleg_cert"
    =:~ grp [10, a stake_credential, a pool_keyhash, a drep]

stake_reg_deleg_cert :: Named Group
stake_reg_deleg_cert =
  "stake_reg_deleg_cert"
    =:~ grp [11, a stake_credential, a pool_keyhash, a coin]

vote_reg_deleg_cert :: Named Group
vote_reg_deleg_cert =
  "vote_reg_deleg_cert"
    =:~ grp [12, a stake_credential, a drep, a coin]

stake_vote_reg_deleg_cert :: Named Group
stake_vote_reg_deleg_cert =
  "stake_vote_reg_deleg_cert"
    =:~ grp [13, a stake_credential, a pool_keyhash, a drep, a coin]

-- GOVCERT
auth_committee_hot_cert :: Named Group
auth_committee_hot_cert =
  "auth_committee_hot_cert"
    =:~ grp [14, a committee_cold_credential, a committee_hot_credential]

resign_committee_cold_cert :: Named Group
resign_committee_cold_cert =
  "resign_committee_cold_cert"
    =:~ grp [15, a committee_cold_credential, a (anchor / VNil)]

reg_drep_cert :: Named Group
reg_drep_cert = "reg_drep_cert" =:~ grp [16, a drep_credential, a coin, a (anchor / VNil)]

unreg_drep_cert :: Named Group
unreg_drep_cert = "unreg_drep_cert" =:~ grp [17, a drep_credential, a coin]

update_drep_cert :: Named Group
update_drep_cert = "update_drep_cert" =:~ grp [18, a drep_credential, a (anchor / VNil)]

delta_coin :: Rule
delta_coin = "delta_coin" =:= VUInt

credential :: Rule
credential =
  "credential"
    =:= arr
      [0, a addr_keyhash]
    / arr [1, a scripthash]

drep :: Rule
drep =
  "drep"
    =:= sarr [0, a addr_keyhash]
    / sarr [1, a scripthash]
    / int 2 -- always abstain
    / int 3 -- always no confidence

stake_credential :: Rule
stake_credential = "stake_credential" =:= credential

drep_credential :: Rule
drep_credential = "drep_credential" =:= credential

committee_cold_credential :: Rule
committee_cold_credential = "committee_cold_credential" =:= credential

committee_hot_credential :: Rule
committee_hot_credential = "committee_hot_credential" =:= credential

pool_params :: Named Group
pool_params =
  "pool_params"
    =:~ grp
      [ "operator" ==> pool_keyhash
      , "vrf_keyhash" ==> vrf_keyhash
      , "pledge" ==> coin
      , "cost" ==> coin
      , "margin" ==> unit_interval
      , "reward_account" ==> reward_account
      , "pool_owners" ==> set addr_keyhash
      , "relays" ==> arr [0 <+ a relay]
      , "pool_metadata" ==> (pool_metadata / VNil)
      ]

port :: Rule
port = "port" =:= VUInt `le` 65535

ipv4 :: Rule
ipv4 = "ipv4" =:= VBytes `sized` (4 :: Word64)

ipv6 :: Rule
ipv6 = "ipv6" =:= VBytes `sized` (16 :: Word64)

dns_name :: Rule
dns_name = "dns_name" =:= VText `sized` (0 :: Word64, 128 :: Word64)

single_host_addr :: Named Group
single_host_addr =
  "single_host_addr"
    =:~ grp
      [ 0
      , a (port / VNil)
      , a (ipv4 / VNil)
      , a (ipv6 / VNil)
      ]

single_host_name :: Named Group
single_host_name =
  "single_host_name"
    =:~ grp
      [ 1
      , a (port / VNil)
      , a dns_name -- An A or AAAA DNS record
      ]

multi_host_name :: Named Group
multi_host_name =
  "multi_host_name"
    =:~ grp
      [ 2
      , a dns_name -- A SRV DNS record
      ]

relay :: Rule
relay =
  "relay"
    =:= arr
      [ a
          ( single_host_addr
              / single_host_name
              / multi_host_name
          )
      ]

pool_metadata :: Rule
pool_metadata = "pool_metadata" =:= arr [a url, a pool_metadata_hash]

url :: Rule
url = "url" =:= VText `sized` (0 :: Word64, 128 :: Word64)

withdrawals :: Rule
withdrawals = "withdrawals" =:= mp [1 <+ asKey reward_account ==> coin]

protocol_param_update :: Rule
protocol_param_update =
  "protocol_param_update"
    =:= mp
      [ opt (idx 0 ==> coin) -- minfee A
      , opt (idx 1 ==> coin) -- minfee B
      , opt (idx 2 ==> VUInt) -- max block body size
      , opt (idx 3 ==> VUInt) -- max transaction size
      , opt (idx 4 ==> VUInt) -- max block header size
      , opt (idx 5 ==> coin) -- key deposit
      , opt (idx 6 ==> coin) -- pool deposit
      , opt (idx 7 ==> epoch) -- maximum epoch
      , opt (idx 8 ==> VUInt) -- n_opt: desired number of stake pools
      , opt (idx 9 ==> nonnegative_interval) -- pool pledge influence
      , opt (idx 10 ==> unit_interval) -- expansion rate
      , opt (idx 11 ==> unit_interval) -- treasury growth rate
      , opt (idx 16 ==> coin) -- min pool cost
      , opt (idx 17 ==> coin) -- ada per utxo byte
      , opt (idx 18 ==> costmdls) -- cost models for script languages
      , opt (idx 19 ==> ex_unit_prices) -- execution costs
      , opt (idx 20 ==> ex_units) -- max tx ex units
      , opt (idx 21 ==> ex_units) -- max block ex units
      , opt (idx 22 ==> VUInt) -- max value size
      , opt (idx 23 ==> VUInt) -- collateral percentage
      , opt (idx 24 ==> VUInt) -- max collateral inputs
      , opt (idx 25 ==> pool_voting_thresholds) -- pool voting thresholds
      , opt (idx 26 ==> drep_voting_thresholds) -- DRep voting thresholds
      , opt (idx 27 ==> VUInt) -- min committee size
      , opt (idx 28 ==> epoch) -- committee term limit
      , opt (idx 29 ==> epoch) -- governance action validity period
      , opt (idx 30 ==> coin) -- governance action deposit
      , opt (idx 31 ==> coin) -- DRep deposit
      , opt (idx 32 ==> epoch) -- DRep inactivity period
      ]

pool_voting_thresholds :: Rule
pool_voting_thresholds =
  "pool_voting_thresholds"
    =:= arr
      [ a unit_interval -- motion no confidence
      , a unit_interval -- committee normal
      , a unit_interval -- committee no confidence
      , a unit_interval -- hard fork initiation
      , a unit_interval -- security relevant parameter voting threshold
      ]

drep_voting_thresholds :: Rule
drep_voting_thresholds =
  "drep_voting_thresholds"
    =:= arr
      [ a unit_interval -- motion no confidence
      , a unit_interval -- committee normal
      , a unit_interval -- committee no confidence
      , a unit_interval -- update constitution
      , a unit_interval -- hard fork initiation
      , a unit_interval -- PP network group
      , a unit_interval -- PP economic group
      , a unit_interval -- PP technical group
      , a unit_interval -- PP governance group
      , a unit_interval -- treasury withdrawal
      ]

transaction_witness_set :: Rule
transaction_witness_set =
  "transaction_witness_set"
    =:= mp
      [ opt $ idx 0 ==> nonempty_set vkeywitness
      , opt $ idx 1 ==> nonempty_set native_script
      , opt $ idx 2 ==> nonempty_set bootstrap_witness
      , opt $ idx 3 ==> nonempty_set plutus_v1_script
      , opt $ idx 4 ==> nonempty_set plutus_data
      , opt $ idx 5 ==> redeemers
      , opt $ idx 6 ==> nonempty_set plutus_v2_script
      , opt $ idx 7 ==> nonempty_set plutus_v3_script
      ]

-- The real type of  plutus_v1_script, plutus_v2_script and plutus_v3_script is bytes.
-- However, because we enforce uniqueness when many scripts are supplied,
-- we need to hack around for tests in order to avoid generating duplicates,
-- since the cddl tool we use for roundtrip testing doesn't generate distinct collections.
plutus_v1_script :: Rule
plutus_v1_script =
  comment
    ( "The real type of  plutus_v1_script, plutus_v2_script and plutus_v3_script is bytes.\n"
        <> "However, because we enforce uniqueness when many scripts are supplied,\n"
        <> "we need to hack around for tests in order to avoid generating duplicates,\n"
        <> "since the cddl tool we use for roundtrip testing doesn't generate distinct collections.\n"
    )
    $ "plutus_v1_script" =:= distinct VBytes

plutus_v2_script :: Rule
plutus_v2_script = "plutus_v2_script" =:= distinct VBytes

plutus_v3_script :: Rule
plutus_v3_script = "plutus_v3_script" =:= distinct VBytes

plutus_data :: Rule
plutus_data =
  "plutus_data"
    =:= constr plutus_data
    / smp [0 <+ asKey plutus_data ==> plutus_data]
    / sarr [0 <+ a plutus_data]
    / big_int
    / bounded_bytes

big_int :: Rule
big_int = "big_int" =:= VInt / big_VUInt / big_nint

big_VUInt :: Rule
big_VUInt = "big_VUInt" =:= tag 2 bounded_bytes

big_nint :: Rule
big_nint = "big_nint" =:= tag 3 bounded_bytes

constr :: IsType0 x => x -> GRuleCall
constr = binding $ \x ->
  "constr"
    =:= tag 1 (arr [0 <+ a x])
    / tag 2 (arr [0 <+ a x])
    / tag 3 (arr [0 <+ a x])
    / tag 4 (arr [0 <+ a x])
    / tag 5 (arr [0 <+ a x])
    / tag 6 (arr [0 <+ a x])
    / tag 7 (arr [0 <+ a x])
    -- similarly for tag range: 6.1280 .. 6.1400 inclusive
    / tag 2 (arr [a VUInt, a $ arr [0 <+ a x]])

redeemers :: Rule
redeemers =
  "redeemers"
    =:= sarr
      [ 1
          <+ a
            ( arr
                [ "tag" ==> redeemer_tag
                , "index" ==> VUInt
                , "data" ==> plutus_data
                , "ex_units" ==> ex_units
                ]
            )
      ]
    / smp
      [ 1
          <+ asKey (mp ["tag" ==> redeemer_tag, "index" ==> VUInt])
          ==> arr ["data" ==> plutus_data, "ex_units" ==> ex_units]
      ]

redeemer_tag :: Rule
redeemer_tag =
  "redeemer_tag"
    =:= int 0 -- Spending
    / int 1 -- Minting
    / int 2 -- Certifying
    / int 3 -- Rewarding
    / int 4 -- Voting
    / int 5 -- Proposing

ex_units :: Rule
ex_units = "ex_units" =:= arr ["mem" ==> VUInt, "steps" ==> VUInt]

ex_unit_prices :: Rule
ex_unit_prices =
  "ex_unit_prices"
    =:= arr
      [ "mem_price" ==> nonnegative_interval
      , "step_price" ==> nonnegative_interval
      ]

language :: Rule
language =
  "language"
    =:= int 0 -- Plutus v1
    / int 1 -- Plutus v2
    / int 2 -- Plutus v3

potential_languages :: Rule
potential_languages = "potential_languages" =:= int 0 ... int 255

-- The format for costmdls is flexible enough to allow adding Plutus built-ins and language
-- versions in the future.
--
costmdls :: Rule
costmdls =
  comment
    "The format for costmdls is flexible enough to allow adding Plutus\n built-ins and language versions in the future."
    $ "costmdls"
      =:= mp
        [ opt $ idx 0 ==> arr [166 <+ a VInt] -- Plutus v1, only 166 integers are used, but more are accepted (and ignored)
        , opt $ idx 1 ==> arr [175 <+ a VInt] -- Plutus v2, only 175 integers are used, but more are accepted (and ignored)
        , opt $ idx 2 ==> arr [223 <+ a VInt] -- Plutus v3, only 223 integers are used, but more are accepted (and ignored)
        , opt $ idx 3 ==> arr [a VInt] -- Any 8-bit unsigned number can be used as a key.
        ]

transaction_metadatum :: Rule
transaction_metadatum =
  "transaction_metadatum"
    =:= smp [0 <+ asKey transaction_metadatum ==> transaction_metadatum]
    / sarr [0 <+ a transaction_metadatum]
    / VInt
    / (VBytes `sized` (0 :: Word64, 64 :: Word64))
    / (VText `sized` (0 :: Word64, 64 :: Word64))

transaction_metadatum_label :: Rule
transaction_metadatum_label = "transaction_metadatum_label" =:= VUInt

metadata :: Rule
metadata =
  "metadata"
    =:= mp
      [ 0
          <+ asKey transaction_metadatum_label
          ==> transaction_metadatum
      ]

auxiliary_data :: Rule
auxiliary_data =
  "auxiliary_data"
    =:= metadata -- Shelley
    / sarr
      [ "transaction_metadata" ==> metadata -- Shelley-ma
      , "auxiliary_scripts" ==> arr [0 <+ a native_script]
      ]
    / smp
      [ opt (idx 0 ==> metadata) -- Alonzo and beyond
      , opt (idx 1 ==> arr [0 <+ a native_script])
      , opt (idx 2 ==> arr [0 <+ a plutus_v1_script])
      , opt (idx 3 ==> arr [0 <+ a plutus_v2_script])
      , opt (idx 4 ==> arr [0 <+ a plutus_v3_script])
      ]

vkeywitness :: Rule
vkeywitness = "vkeywitness" =:= arr [a vkey, a signature]

bootstrap_witness :: Rule
bootstrap_witness =
  "bootstrap_witness"
    =:= arr
      [ "public_key" ==> vkey
      , "signature" ==> signature
      , "chain_code" ==> (VBytes `sized` (32 :: Word64))
      , "attributes" ==> VBytes
      ]

native_script :: Rule
native_script =
  "native_script"
    =:= arr
      [ a
          ( script_pubkey
              / script_all
              / script_any
              / script_n_of_k
              / invalid_before
              -- Timelock validity intervals are half-open intervals [a, b).
              -- This field specifies the left (included) endpoint a.
              / invalid_hereafter
              -- Timelock validity intervals are half-open intervals [a, b).
              -- This field specifies the right (excluded) endpoint b.
          )
      ]

script_pubkey :: Named Group
script_pubkey = "script_pubkey" =:~ grp [0, a addr_keyhash]

script_all :: Named Group
script_all = "script_all" =:~ grp [1, a (arr [0 <+ a native_script])]

script_any :: Named Group
script_any = "script_any" =:~ grp [2, a (arr [0 <+ a native_script])]

script_n_of_k :: Named Group
script_n_of_k =
  "script_n_of_k"
    =:~ grp [3, "n" ==> VUInt, a (arr [0 <+ a native_script])]

invalid_before :: Named Group
invalid_before = "invalid_before" =:~ grp [4, a VUInt]

invalid_hereafter :: Named Group
invalid_hereafter = "invalid_hereafter" =:~ grp [5, a VUInt]

coin :: Rule
coin = "coin" =:= VUInt

multiasset :: (Show a, IsType0 a) => a -> Rule
multiasset x =
  "multiasset_"
    <> T.pack (show x)
      =:= mp [1 <+ asKey policy_id ==> mp [1 <+ asKey asset_name ==> x]]

policy_id :: Rule
policy_id = "policy_id" =:= scripthash

asset_name :: Rule
asset_name = "asset_name" =:= VBytes `sized` (0 :: Word64, 32 :: Word64)

negInt64 :: Rule
negInt64 = "negInt64" =:= int (-9223372036854775808) ... int (-1)

posInt64 :: Rule
posInt64 = "posInt64" =:= int 1 ... int 9223372036854775807

nonZeroInt64 :: Rule
nonZeroInt64 = "nonZeroInt64" =:= negInt64 / posInt64 -- this is the same as the current int64 definition but without zero

positive_coin :: Rule
positive_coin = "positive_coin" =:= int 1 ... int 18446744073709551615

value :: Rule
value = "value" =:= coin / sarr [a coin, a (multiasset positive_coin)]

mint :: Rule
mint = "mint" =:= multiasset nonZeroInt64

int64 :: Rule
int64 = "int64" =:= int (-9223372036854775808) ... int 9223372036854775807

network_id :: Rule
network_id = "network_id" =:= int 0 / int 1

epoch :: Rule
epoch = "epoch" =:= VUInt

addr_keyhash :: Rule
addr_keyhash = "addr_keyhash" =:= hash28

pool_keyhash :: Rule
pool_keyhash = "pool_keyhash" =:= hash28

vrf_keyhash :: Rule
vrf_keyhash = "vrf_keyhash" =:= hash32

auxiliary_data_hash :: Rule
auxiliary_data_hash = "auxiliary_data_hash" =:= hash32

pool_metadata_hash :: Rule
pool_metadata_hash = "pool_metadata_hash" =:= hash32

-- To compute a script hash, note that you must prepend
-- a tag to the bytes of the script before hashing.
-- The tag is determined by the language.
-- The tags in the Conway era are:
--   "\x00" for multisig scripts
--   "\x01" for Plutus V1 scripts
--   "\x02" for Plutus V2 scripts
--   "\x03" for Plutus V3 scripts
scripthash :: Rule
scripthash =
  comment
    ( "To compute a script hash, note that you must prepend\n"
        <> "a tag to the bytes of the script before hashing.\n"
        <> "The tag is determined by the language.\n"
        <> "The tags in the Conway era are:\n"
        <> "\"\x00\" for multisig scripts\n"
        <> "\"\x01\" for Plutus V1 scripts\n"
        <> "\"\x02\" for Plutus V2 scripts\n"
        <> "\"\x03\" for Plutus V3 scripts\n"
    )
    $ "scripthash" =:= hash28

datum_hash :: Rule
datum_hash = "datum_hash" =:= hash32

data_a :: Rule
data_a = "data" =:= VBytes `cbor` plutus_data

datum_option :: Rule
datum_option = "datum_option" =:= arr [0, a hash32] / arr [1, a data_a]

script_ref :: Rule
script_ref = "script_ref" =:= VBytes `cbor` script

script :: Rule
script =
  "script"
    =:= arr [0, a native_script]
    / arr [1, a plutus_v1_script]
    / arr [2, a plutus_v2_script]
    / arr [3, a plutus_v3_script]

--------------------------------------------------------------------------------
-- Crypto
--------------------------------------------------------------------------------

hash28 :: Rule
hash28 = "hash28" =:= VBytes `sized` (28 :: Word64)

hash32 :: Rule
hash32 = "hash32" =:= VBytes `sized` (32 :: Word64)

vkey :: Rule
vkey = "vkey" =:= VBytes `sized` (32 :: Word64)

vrf_vkey :: Rule
vrf_vkey = "vrf_vkey" =:= VBytes `sized` (32 :: Word64)

vrf_cert :: Rule
vrf_cert = "vrf_cert" =:= arr [a VBytes, a (VBytes `sized` (80 :: Word64))]

kes_vkey :: Rule
kes_vkey = "kes_vkey" =:= VBytes `sized` (32 :: Word64)

kes_signature :: Rule
kes_signature = "kes_signature" =:= VBytes `sized` (448 :: Word64)

signkeyKES :: Rule
signkeyKES = "signkeyKES" =:= VBytes `sized` (64 :: Word64)

signature :: Rule
signature = "signature" =:= VBytes `sized` (64 :: Word64)

--------------------------------------------------------------------------------
-- Extras
--------------------------------------------------------------------------------

-- Conway era introduces an optional 258 tag for sets, which will become mandatory in the
-- second era after Conway. We recommend all the tooling to account for this future breaking
-- change sooner rather than later, in order to provide a smooth transition for their users.

set :: IsType0 t0 => t0 -> GRuleCall
set = binding $ \x -> "set" =:= arr [0 <+ a x]

nonempty_set :: IsType0 t0 => t0 -> GRuleCall
nonempty_set = binding $ \x -> "nonempty_set" =:= set x

-- TODO Should we give this a name?
nonempty_oset :: IsType0 t0 => t0 -> GRuleCall
nonempty_oset = nonempty_set

positive_int :: Rule
positive_int = "positive_int" =:= int 1 ... int 18446744073709551615

unit_interval :: Rule
unit_interval = "unit_interval" =:= tag 30 (arr [1, 2])

-- unit_interval = tag 0 [uint, uint]
--
-- Comment above depicts the actual definition for `unit_interval`.
--
-- Unit interval is a number in the range between 0 and 1, which
-- means there are two extra constraints:
-- \* numerator <= denominator
-- \* denominator > 0
--
-- Relation between numerator and denominator cannot be expressed in CDDL, which
-- poses a problem for testing. We need to be able to generate random valid data
-- for testing implementation of our encoders/decoders. Which means we cannot use
-- the actual definition here and we hard code the value to 1/2

-- nonnegative_interval = tag 0 [uint, positive_int]
nonnegative_interval :: Rule
nonnegative_interval = "nonnegative_interval" =:= tag 30 (arr [a VUInt, a positive_int])

address :: Rule
address =
  "address"
    =:= bstr
      "001000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000"
    / bstr
      "102000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000"
    / bstr
      "203000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000"
    / bstr
      "304000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000"
    / bstr "405000000000000000000000000000000000000000000000000000000087680203"
    / bstr "506000000000000000000000000000000000000000000000000000000087680203"
    / bstr "6070000000000000000000000000000000000000000000000000000000"
    / bstr "7080000000000000000000000000000000000000000000000000000000"

reward_account :: Rule
reward_account =
  "reward_account"
    =:= bstr "E090000000000000000000000000000000000000000000000000000000"
    / bstr "F0A0000000000000000000000000000000000000000000000000000000"

bounded_bytes :: Rule
bounded_bytes = "bounded_bytes" =:= VBytes `sized` (0 :: Word64, 64 :: Word64)

-- the real bounded_bytes does not have this limit. it instead has a different
-- limit which cannot be expressed in CDDL.
-- The limit is as follows:
--  - bytes with a definite-length encoding are limited to size 0..64
--  - for bytes with an indefinite-length CBOR encoding, each chunk is
--    limited to size 0..64
--  ( reminder: in CBOR, the indefinite-length encoding of bytestrings
--    consists of a token #2.31 followed by a sequence of definite-length
--    encoded bytestrings and a stop code )

-- a type for distinct values.
-- The type parameter must support .size, for example: bytes or uint

distinct :: (IsType0 a, IsConstrainable a b) => a -> GRuleCall
distinct = binding $ \x ->
  "distinct"
    =:= (x `sized` (8 :: Word64))
    / (x `sized` (16 :: Word64))
    / (x `sized` (20 :: Word64))
    / (x `sized` (24 :: Word64))
    / (x `sized` (30 :: Word64))
    / (x `sized` (32 :: Word64))