Skip to content

Commit e684316

Browse files
author
euonymos
committed
Merge branch 'master' into alexey/test-oura-filters
2 parents a83073b + 1984e82 commit e684316

File tree

27 files changed

+1874
-957
lines changed

27 files changed

+1874
-957
lines changed

.ghcid

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
--command="cabal repl test-suite:cem-sdk-test" -W -T ":main --failure-report=/tmp/hspec-report.txt -r"
1+
--command="cabal repl test-suite:cem-sdk-test" -W -T ":main"

.hlint.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@
1616
- ignore: {name: Use unless}
1717
- ignore: {name: "Use asks"}
1818
- ignore: {name: "Eta reduce"}
19+
- ignore: {name: Use concatMap}

cem-script.cabal

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ common common-lang
3737
build-depends:
3838
, base
3939
, extra
40+
, containers
4041
, mtl
4142
, transformers
4243

@@ -50,9 +51,10 @@ common common-lang
5051
GADTs
5152
LambdaCase
5253
NoImplicitPrelude
53-
NoPolyKinds
54+
OverloadedRecordDot
5455
OverloadedStrings
5556
PatternSynonyms
57+
PolyKinds
5658
QuantifiedConstraints
5759
StrictData
5860
TemplateHaskell
@@ -104,11 +106,12 @@ common common-offchain
104106
, cardano-ledger-babbage
105107
, cardano-ledger-core
106108
, cardano-ledger-shelley
107-
, containers
108109
, filepath
109110
, ouroboros-network-protocols
110111
, pretty-show
112+
, prettyprinter
111113
, retry
114+
, singletons-th
112115
, text
113116
, time
114117
, unix
@@ -120,7 +123,10 @@ common common-executable
120123
library data-spine
121124
import: common-lang
122125
hs-source-dirs: src-lib/data-spine
126+
127+
-- FIXME: was not meant to be dependent on Plutus...
123128
build-depends:
129+
, plutus-tx
124130
, singletons
125131
, template-haskell
126132

@@ -135,6 +141,7 @@ library cardano-extras
135141
build-depends: template-haskell
136142
exposed-modules:
137143
Cardano.Extras
144+
Plutarch.Extras
138145
Plutus.Extras
139146

140147
library
@@ -147,6 +154,7 @@ library
147154
Cardano.CEM
148155
Cardano.CEM.Address
149156
Cardano.CEM.Documentation
157+
Cardano.CEM.DSL
150158
Cardano.CEM.Examples.Auction
151159
Cardano.CEM.Examples.Compilation
152160
Cardano.CEM.Examples.Voting

docs/catalyst_milestone_reports.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,31 @@
1010
* Final code clean-up
1111
* Final tutorial and docs
1212

13+
## Clarifications on M3 Deliverables
14+
15+
### Running an Emulated Environment by CLB
16+
17+
The CLB monad is designed to interact seamlessly with a Cardano mockchain environment. It can read from a shared environment, query blockchain parameters such as the current slot, and retrieve UTXO information, among other functions. This module facilitates simulation and testing, making it essential for unit tests and other testing scenarios where a controlled blockchain environment is needed. Using this emulated environment, developers can test and validate their Cardano applications in a reliable and repeatable manner.
18+
19+
### Running QuickCheck Dynamic Tests, Including Mutation Support
20+
21+
QuickCheck is a powerful Haskell library for property-based testing, which helps ensure that programs behave correctly for a wide range of inputs. The quickcheck-dynamic library extends this tool to stateful systems, making it particularly suitable for testing blockchain applications. Our testing framework uses a state machine model to simulate real-world scenarios, incorporating support for mutation testing. This approach helps verify that the system maintains correct behavior under various conditions, including edge cases and unexpected changes, thereby enhancing the robustness of the application.
22+
23+
### Rendering CEMScript State Graphs
24+
25+
Understanding state transitions and the overall system flow is critical for ensuring blockchain applications do not enter invalid states. To aid developers, we have implemented automated rendering of state graphs through our documentation module. This module generates DOT graph representations of CEMScript state transitions, providing an easy-to-understand visual model of how a DApp functions in real-world scenarios. This visualization tool increases developer confidence by highlighting the system's behavior and identifying potential shortcomings.
26+
1327
# Milestone 4
1428

1529
## Summary
1630

1731
Catalyst Milestone Outputs:
1832

19-
* Plutarch on-chain code generation and optimisation ([PR](https://github.com/mlabs-haskell/cem-script/pull/94))
33+
* Static DSL for constraints and Plutarch on-chain code generation and optimisation ([PR#96](https://github.com/mlabs-haskell/cem-script/pull/96))
2034
* Profiling implementation ([PR](https://github.com/mlabs-haskell/cem-script/pull/95))
2135
* Profiling performed for old (using Plutus) and new (using Plutarch) code generator.
2236
New one showed advantage over first one in all fees measured, and in some cases in order of magntude.
37+
* A [video](https://www.youtube.com/watch?v=wveLyvuKjeI) that summarizes the outcomes achieved in the milestone.
2338

2439
## Profiling results
2540

docs/tech_debt.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
* Design
2+
* Tx Signers
3+
* Tx stuff naming
4+
* Tests
5+
* Mutation and security
6+
* Code arch and style
7+
* No onchain/offchain GHC options separations of code
8+
* No hlint
9+
* CI and versioning
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{-# LANGUAGE QualifiedDo #-}
2+
3+
module Plutarch.Extras where
4+
5+
import Prelude
6+
7+
import Plutarch
8+
import Plutarch.Builtin
9+
import Plutarch.LedgerApi
10+
import Plutarch.LedgerApi.Value
11+
import Plutarch.Maybe (pfromJust)
12+
import Plutarch.Monadic qualified as P
13+
import Plutarch.Prelude
14+
15+
pMkAdaOnlyValue :: Term s (PInteger :--> PValue Unsorted NonZero)
16+
pMkAdaOnlyValue = phoistAcyclic $ plam $ \lovelaces ->
17+
pforgetSorted $
18+
psingletonData # padaSymbolData # pdata padaToken # pdata lovelaces
19+
20+
pscriptHashAddress :: Term s (PAsData PScriptHash :--> PAddress)
21+
pscriptHashAddress = plam $ \datahash ->
22+
let credential = pcon (PScriptCredential (pdcons @"_0" # datahash #$ pdnil))
23+
nothing = pdata $ pcon (PDNothing pdnil)
24+
inner = pdcons @"credential" # pdata credential #$ pdcons @"stakingCredential" # nothing #$ pdnil
25+
in pcon (PAddress inner)
26+
27+
ppkhAddress :: Term s (PAsData PPubKeyHash :--> PAddress)
28+
ppkhAddress = plam $ \datahash ->
29+
let credential = pcon (PPubKeyCredential (pdcons @"_0" # datahash #$ pdnil))
30+
nothing = pdata $ pcon (PDNothing pdnil)
31+
inner = pdcons @"credential" # pdata credential #$ pdcons @"stakingCredential" # nothing #$ pdnil
32+
in pcon (PAddress inner)
33+
34+
getOwnAddress :: ClosedTerm (PAsData PScriptContext :--> PAsData PAddress)
35+
getOwnAddress = phoistAcyclic $ plam $ \ctx -> P.do
36+
PSpending outRef' <- pmatch $ pfromData $ pfield @"purpose" # ctx
37+
pfield @"address"
38+
#$ pfield @"resolved"
39+
#$ pfromJust
40+
#$ (pfindOwnInput # (pfield @"inputs" #$ pfield @"txInfo" # ctx))
41+
#$ pfield @"_0"
42+
# outRef'

src-lib/data-spine/Data/Spine.hs

Lines changed: 95 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,29 @@
33
{-# LANGUAGE DefaultSignatures #-}
44
{-# LANGUAGE PolyKinds #-}
55

6-
module Data.Spine (HasSpine (..), deriveSpine, OfSpine (..)) where
6+
{- |
7+
Note about design decision on nested spines.
8+
`getSpine (Just Value) = JustSpine ValueSpine` - looks more usable,
9+
than `getSpine (Just Value) = JustSpine`.
10+
But it seem to break deriving for parametised types like `Maybe a`,
11+
and can be done with `fmap getSpine mValue`. Probably it actually
12+
works exaclty for functorial parameters.
13+
-}
14+
module Data.Spine where
715

816
import Prelude
917

18+
import Data.Data (Proxy)
19+
import Data.List (elemIndex)
20+
import Data.Map qualified as Map
21+
import Data.Maybe (mapMaybe)
22+
import GHC.Natural (Natural)
23+
import GHC.TypeLits (KnownSymbol, symbolVal)
1024
import Language.Haskell.TH
1125
import Language.Haskell.TH.Syntax
1226

27+
import PlutusTx (FromData, ToData, UnsafeFromData, unstableMakeIsData)
28+
1329
-- | Definitions
1430

1531
{- | Spine is datatype, which tags only constructors of ADT skipping their content.
@@ -19,29 +35,66 @@ import Language.Haskell.TH.Syntax
1935
class
2036
( Ord (Spine sop)
2137
, Show (Spine sop)
38+
, Enum (Spine sop)
39+
, Bounded (Spine sop)
2240
) =>
2341
HasSpine sop
2442
where
25-
type Spine sop
43+
type Spine sop = spine | spine -> sop
2644
getSpine :: sop -> Spine sop
2745

28-
instance (HasSpine sop1, HasSpine sop2) => HasSpine (sop1, sop2) where
29-
type Spine (sop1, sop2) = (Spine sop1, Spine sop2)
30-
getSpine (d1, d2) = (getSpine d1, getSpine d2)
46+
-- | Version of `HasSpine` knowing its Plutus Data encoding
47+
class
48+
( HasSpine sop
49+
, UnsafeFromData sop
50+
, ToData sop
51+
, FromData sop
52+
) =>
53+
HasPlutusSpine sop
54+
where
55+
fieldsMap :: Map.Map (Spine sop) [String]
56+
57+
toNat :: Int -> Natural
58+
toNat = fromInteger . toInteger
59+
60+
spineFieldsNum :: forall sop. (HasPlutusSpine sop) => Spine sop -> Natural
61+
spineFieldsNum spine =
62+
toNat $ length $ (fieldsMap @sop) Map.! spine
63+
64+
-- FIXME: use spine do discriminate
65+
fieldNum ::
66+
forall sop label.
67+
(HasPlutusSpine sop, KnownSymbol label) =>
68+
Proxy label ->
69+
Natural
70+
fieldNum proxyLabel =
71+
head $ mapMaybe fieldIndex x
72+
where
73+
x = Map.elems $ fieldsMap @sop
74+
fieldName = symbolVal proxyLabel
75+
fieldIndex dict = toNat <$> elemIndex fieldName dict
3176

32-
instance (HasSpine sop) => HasSpine (Maybe sop) where
33-
type Spine (Maybe sop) = Maybe (Spine sop)
34-
getSpine = fmap getSpine
77+
allSpines :: forall sop. (HasPlutusSpine sop) => [Spine sop]
78+
allSpines = [Prelude.minBound .. Prelude.maxBound]
3579

36-
-- | Newtype encoding sop value of fixed known spine
37-
newtype OfSpine (x :: Spine datatype) = UnsafeMkOfSpine {getValue :: datatype}
80+
-- | Phantom type param is required for `HasSpine` injectivity
81+
data MaybeSpine a = JustSpine | NothingSpine
82+
deriving stock (Eq, Ord, Show, Bounded, Enum)
83+
84+
-- FIXME: could such types be derived?
85+
instance HasSpine (Maybe x) where
86+
type Spine (Maybe x) = MaybeSpine x
87+
getSpine Just {} = JustSpine
88+
getSpine Nothing = NothingSpine
89+
90+
-- Deriving utils
3891

39-
-- | Deriving utils
4092
addSuffix :: Name -> String -> Name
4193
addSuffix (Name (OccName name) flavour) suffix =
4294
Name (OccName $ name <> suffix) flavour
4395

44-
reifyDatatype :: Name -> Q (Name, [Name])
96+
-- FIXME: cleaner return type
97+
reifyDatatype :: Name -> Q (Name, [Name], [[Name]])
4598
reifyDatatype ty = do
4699
(TyConI tyCon) <- reify ty
47100
(name, cs :: [Con]) <-
@@ -50,7 +103,17 @@ reifyDatatype ty = do
50103
NewtypeD _ n _ _ cs _ -> pure (n, [cs])
51104
_ -> fail "deriveTags: only 'data' and 'newtype' are supported"
52105
csNames <- mapM consName cs
53-
return (name, csNames)
106+
csFields <- mapM consFields cs
107+
return (name, csNames, csFields)
108+
where
109+
fieldName (name, _, _) = name
110+
consFields (RecC _ fields) = return $ map fieldName fields
111+
consFields (NormalC _ fields) | length fields == 0 = return []
112+
consFields _ =
113+
fail $
114+
"Spine: only Sum-of-Products are supported, but "
115+
<> show ty
116+
<> " is not"
54117

55118
consName :: (MonadFail m) => Con -> m Name
56119
consName cons =
@@ -61,7 +124,7 @@ consName cons =
61124

62125
deriveTags :: Name -> String -> [Name] -> Q [Dec]
63126
deriveTags ty suff classes = do
64-
(tyName, csNames) <- reifyDatatype ty
127+
(tyName, csNames, _) <- reifyDatatype ty
65128
-- XXX: Quasi-quote splice does not work for case matches list
66129
let cs = map (\name -> NormalC (addSuffix name suff) []) csNames
67130
v =
@@ -70,7 +133,7 @@ deriveTags ty suff classes = do
70133

71134
deriveMapping :: Name -> String -> Q Exp
72135
deriveMapping ty suff = do
73-
(_, csNames) <- reifyDatatype ty
136+
(_, csNames, _) <- reifyDatatype ty
74137
-- XXX: Quasi-quote splice does not work for case matches list
75138
let
76139
matches =
@@ -87,7 +150,7 @@ deriveSpine name = do
87150
let
88151
suffix = "Spine"
89152
spineName = addSuffix name suffix
90-
spineDec <- deriveTags name suffix [''Eq, ''Ord, ''Enum, ''Show]
153+
spineDec <- deriveTags name suffix [''Eq, ''Ord, ''Enum, ''Show, ''Bounded]
91154

92155
decls <-
93156
[d|
@@ -96,3 +159,19 @@ deriveSpine name = do
96159
getSpine = $(deriveMapping name suffix)
97160
|]
98161
return $ spineDec <> decls
162+
163+
derivePlutusSpine :: Name -> Q [Dec]
164+
derivePlutusSpine name = do
165+
decls <- deriveSpine name
166+
isDataDecls <- unstableMakeIsData name
167+
168+
(_, _, fieldsNames') <- reifyDatatype name
169+
let fieldsNames = map (map nameBase) fieldsNames'
170+
instanceDecls <-
171+
[d|
172+
instance HasPlutusSpine $(conT name) where
173+
fieldsMap =
174+
Map.fromList $ zip (allSpines @($(conT name))) fieldsNames
175+
|]
176+
177+
return $ decls <> isDataDecls <> instanceDecls

0 commit comments

Comments
 (0)