Skip to content

Commit a83073b

Browse files
authored
Merge pull request #106 from mlabs-haskell/euonymos/rip-transition-spine
Rip transition spines from transactions
2 parents baee6d0 + c08a64d commit a83073b

File tree

28 files changed

+1027
-953
lines changed

28 files changed

+1027
-953
lines changed

cabal.project

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ source-repository-package
2222
tag: d5b0e7ce07258482d53704ce19383013b1fa6610
2323
--sha256: 6+Os/mQDzBOU+TkTD+n/T1MFcI+Mn0/tcBMJhLRfqyA=
2424

25-
-- FIXME: Cannot use new commit, because it requires `plutus-ledger-api==1.29`
25+
-- Cannot use new commit, because it requires `plutus-ledger-api==1.29`
2626
source-repository-package
2727
type: git
2828
location: https://github.com/Plutonomicon/plutarch-plutus

cem-script.cabal

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version: 0.1.0
44
synopsis: CEM Script - a Cardano dApp SDK
55
homepage: https://github.com/mlabs-haskell/cem-script
66
author: MLabs
7-
maintainer: gregory@mlabs.city
7+
maintainer: ilia@mlabs.city
88
data-files: README.md
99
tested-with: GHC ==9.6.3
1010

@@ -15,9 +15,11 @@ flag dev
1515
default: True
1616
manual: False
1717

18-
common common-lang
19-
-- Options from MLabs styleguide
18+
flag force-recomp
19+
description: Compile with -fforce-recomp and -Wunused-packages
20+
default: False
2021

22+
common common-lang
2123
ghc-options:
2224
-Wall -Wcompat -Wincomplete-record-updates
2325
-Wincomplete-uni-patterns -Wredundant-constraints
@@ -26,8 +28,15 @@ common common-lang
2628
if !flag(dev)
2729
ghc-options: -Werror
2830

31+
if flag(dev)
32+
default-extensions: PartialTypeSignatures
33+
34+
if flag(force-recomp)
35+
ghc-options: -fforce-recomp -Wunused-packages
36+
2937
build-depends:
3038
, base
39+
, extra
3140
, mtl
3241
, transformers
3342

@@ -52,9 +61,6 @@ common common-lang
5261
UndecidableInstances
5362
ViewPatterns
5463

55-
if flag(dev)
56-
default-extensions: PartialTypeSignatures
57-
5864
default-language: GHC2021
5965

6066
common common-onchain
@@ -144,26 +150,32 @@ library
144150
Cardano.CEM.Examples.Auction
145151
Cardano.CEM.Examples.Compilation
146152
Cardano.CEM.Examples.Voting
147-
Cardano.CEM.Indexing
153+
Cardano.CEM.Indexing.Event
154+
Cardano.CEM.Indexing.Oura
155+
Cardano.CEM.Indexing.Tx
148156
Cardano.CEM.Monads
149157
Cardano.CEM.Monads.CLB
150-
Cardano.CEM.Monads.L1
151158
Cardano.CEM.OffChain
152159
Cardano.CEM.OnChain
153160
Cardano.CEM.Testing.StateMachine
154161
Cardano.CEM.TH
155162

156163
other-modules: Cardano.CEM.Monads.L1Commons
157164
build-depends:
165+
, base16
166+
, base64
158167
, cem-script:cardano-extras
159168
, cem-script:data-spine
160169
, clb
161170
, dependent-map
171+
, lens
162172
, ouroboros-consensus
163173
, QuickCheck
164174
, quickcheck-dynamic
175+
, safe
165176
, singletons-th
166177
, toml-parser
178+
, vector
167179

168180
test-suite cem-sdk-test
169181
import:
@@ -205,12 +217,10 @@ test-suite cem-sdk-test
205217
Auction
206218
Dynamic
207219
OffChain
208-
Oura
209220
Oura.Communication
210-
Oura.Config
211-
OuraFilters
212221
OuraFilters.Auction
213222
OuraFilters.Mock
223+
OuraFilters.Simple
214224
TestNFT
215225
Utils
216226
Voting

src-lib/cardano-extras/Cardano/Extras.hs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,6 @@ mintedTokens ::
275275
[(AssetName, Quantity)] ->
276276
Cardano.TxMintValue BuildTx Era
277277
mintedTokens script redeemer assets =
278-
-- FIXME: is hardcoding era correct?
279278
TxMintValue Cardano.MaryEraOnwardsBabbage mintedTokens' mintedWitnesses'
280279
where
281280
mintedTokens' = valueFromList (fmap (first (AssetId policyId)) assets)

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import Language.Haskell.TH.Syntax
1212

1313
-- | Definitions
1414

15-
{- | Spine is datatype, which tags constructors of ADT.
16-
| TH deriving utility generates Spines, which are Enums,
17-
| but one could introduce more complex Spine datatypes manually.
15+
{- | Spine is datatype, which tags only constructors of ADT skipping their content.
16+
TH deriving utility generates Spines which are Enums but one could introduce
17+
more complex Spine datatypes manually.
1818
-}
1919
class
2020
( Ord (Spine sop)
@@ -88,8 +88,6 @@ deriveSpine name = do
8888
suffix = "Spine"
8989
spineName = addSuffix name suffix
9090
spineDec <- deriveTags name suffix [''Eq, ''Ord, ''Enum, ''Show]
91-
-- TODO: derive Sing
92-
-- TODO: derive HasField (OfSpine ...)
9391

9492
decls <-
9593
[d|

src/Cardano/CEM.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ class
111111
type EqShow datatype =
112112
( Prelude.Eq datatype
113113
, Prelude.Show datatype
114-
-- TODO: add IsData here? (now it breaks Plutus compilation)
114+
-- Shoul we add IsData here? (now it breaks Plutus compilation)
115115
)
116116

117117
{- | All associated types for 'CEMScript' class defined separately to simplify
@@ -223,7 +223,7 @@ data TxFanKind
223223
-- | Constraint on a single tx fan
224224
data TxFanFilter script = MkTxFanFilter
225225
{ address :: AddressSpec
226-
, rest :: FilterDatum script -- TODO: not ideal naming
226+
, datumFilter :: FilterDatum script
227227
}
228228
deriving stock (Show, Prelude.Eq)
229229

@@ -252,8 +252,8 @@ bySameCEM = UnsafeBySameCEM . toBuiltinData
252252

253253
-- | How many tx fans should satify a 'TxFansConstraint'
254254
data Quantifier
255-
= ExactlyNFans Integer -- TODO: use natural numbers
256-
| FansWithTotalValueOfAtLeast Value -- TODO: use natural numbers
255+
= ExactlyNFans Integer -- Here we'd better use natural numbers
256+
| FansWithTotalValueOfAtLeast Value
257257
deriving stock (Show)
258258

259259
-- | A constraint on Tx inputs or Outputs.

src/Cardano/CEM/Address.hs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Cardano.CEM.Address (
2-
cardanoAddressBech32,
2+
scriptCredential,
33
scriptCardanoAddress,
4+
cardanoAddressBech32,
45
plutusAddressToShelleyAddress,
56
AddressBech32 (MkAddressBech32, unAddressBech32),
67
) where
@@ -14,7 +15,7 @@ import Cardano.Ledger.BaseTypes qualified as Ledger
1415
import Cardano.Ledger.Credential qualified as Cred
1516
import Cardano.Ledger.Hashes qualified
1617
import Cardano.Ledger.Keys qualified as Ledger.Keys
17-
import Data.Data (Proxy (Proxy))
18+
import Data.Proxy (Proxy)
1819
import Data.String (IsString)
1920
import Data.Text qualified as T
2021
import Plutus.Extras qualified
@@ -33,13 +34,22 @@ scriptCardanoAddress ::
3334
Proxy script ->
3435
Cardano.Api.Ledger.Network ->
3536
Either String (Cardano.Api.Address Cardano.Api.ShelleyAddr)
36-
scriptCardanoAddress _ network =
37+
scriptCardanoAddress p network =
3738
plutusAddressToShelleyAddress network
3839
. flip PlutusLedgerApi.V1.Address Nothing
39-
. PlutusLedgerApi.V1.ScriptCredential
40+
. scriptCredential
41+
$ p
42+
43+
scriptCredential ::
44+
forall script.
45+
(Compiled.CEMScriptCompiled script) =>
46+
Proxy script ->
47+
PlutusLedgerApi.V1.Credential
48+
scriptCredential p =
49+
PlutusLedgerApi.V1.ScriptCredential
4050
. Plutus.Extras.scriptValidatorHash
4151
. Compiled.cemScriptCompiled
42-
$ Proxy @script
52+
$ p
4353

4454
plutusAddressToShelleyAddress ::
4555
Cardano.Api.Ledger.Network ->

src/Cardano/CEM/Examples.hs

Whitespace-only changes.

src/Cardano/CEM/Examples/Auction.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{-# LANGUAGE NoPolyKinds #-}
22

3+
-- {-# OPTIONS_GHC -Wno-unrecognised-pragmas -ddump-splices #-}
4+
35
module Cardano.CEM.Examples.Auction where
46

57
import PlutusTx.Prelude

src/Cardano/CEM/Indexing/Event.hs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
2+
3+
-- | Indexer events, i.e. indexer outputs.
4+
module Cardano.CEM.Indexing.Event where
5+
6+
import Cardano.Api qualified as C
7+
import Cardano.Api.ScriptData qualified as C
8+
import Cardano.Api.SerialiseRaw qualified as SerialiseRaw
9+
import Cardano.CEM (CEMScript, CEMScriptDatum, State, Transition, transitionStage)
10+
import Cardano.CEM.Address qualified as Address
11+
import Cardano.CEM.Indexing.Tx
12+
import Cardano.CEM.OnChain (CEMScriptCompiled, CEMScriptIsData)
13+
import Cardano.Ledger.BaseTypes qualified as Ledger
14+
import Control.Lens (view, (^.))
15+
import Data.Bifunctor (first)
16+
import Data.ByteString.Base16 qualified as B16
17+
import Data.Data (Proxy (Proxy))
18+
import Data.Either.Extra (eitherToMaybe)
19+
import Data.Function ((&))
20+
import Data.List (find)
21+
import Data.Map.Strict qualified as Map
22+
import Data.Maybe (fromJust)
23+
import Data.Spine (Spine, getSpine)
24+
import Data.Text.Encoding (encodeUtf8)
25+
import Data.Tuple (swap)
26+
import PlutusLedgerApi.V1 (FromData)
27+
import PlutusLedgerApi.V1 qualified
28+
import Prelude
29+
30+
-- ---
31+
32+
{- | Indexer events.
33+
We extract events from transactions, where we can encounter three situations:
34+
35+
(1) For the very first transition there is only target datum and no redeemer.
36+
In that case we can only restore the name of the transition,
37+
i.e. 'Spine Transition'
38+
39+
(2) For intermidiate transitions we have both datums that identify them and
40+
additionally redeemer, that contains the whole transition. In that case
41+
we can restore the whole transition.
42+
43+
(3) For the final transition the situation is like (2) except the target
44+
datum is missing, which doesn't matter.
45+
46+
TODO: How we can improve this in the future:
47+
* API is probably bad, as we always have some transition like Init state -
48+
which you can decode, as you have State. If one changes data
49+
`CEMAction script = MkCEMAction (Params script) (Transition script)` to
50+
`... = Init (Params script) (State script)
51+
| Transition (Params script) (Transition script)`
52+
one could reuse this datatype in all situations.
53+
-}
54+
data IndexerEvent script
55+
= Initial (Spine (Transition script))
56+
| -- | TODO: Migrate from (Spine (Transition script)) to (Transition script)
57+
-- once we have this done: https://github.com/utxorpc/spec/issues/132
58+
Following (Spine (Transition script)) -- (Transition script)
59+
60+
deriving stock instance
61+
(Show (Spine (Transition script))) =>
62+
(Show (IndexerEvent script))
63+
deriving stock instance
64+
(Eq (Spine (Transition script))) =>
65+
(Eq (IndexerEvent script))
66+
67+
{- | The core function, that extracts an Event out of a Oura transaction.
68+
It might be a pure function, IO here was used mostly to simplify debugging
69+
during its development.
70+
-}
71+
extractEvent ::
72+
forall script.
73+
( CEMScript script
74+
, CEMScriptIsData script
75+
, CEMScriptCompiled script
76+
) =>
77+
Ledger.Network ->
78+
Tx ->
79+
IO (Maybe (IndexerEvent script))
80+
extractEvent network tx = do
81+
-- Script payemnt credential based predicate
82+
let ~(Right scriptAddr) = Address.scriptCardanoAddress (Proxy @script) network
83+
let cPred = hasAddr scriptAddr
84+
85+
-- Source state
86+
let mOwnInput :: Maybe TxInput = find (cPred . view as_output) (tx ^. inputs)
87+
let mSourceState :: Maybe (State script) = (extractState . view as_output) =<< mOwnInput
88+
let mSourceSpine :: Maybe (Spine (State script)) = getSpine <$> mSourceState
89+
90+
-- Target state
91+
let mOwnOutput :: Maybe TxOutput = find cPred $ tx ^. outputs
92+
let mTargetState :: Maybe (State script) = extractState =<< mOwnOutput
93+
let mTargetSpine :: Maybe (Spine (State script)) = getSpine <$> mTargetState
94+
95+
-- Look up the transition
96+
let transitions =
97+
first
98+
(\(_, b, c) -> (b, c))
99+
. swap
100+
<$> Map.toList (transitionStage $ Proxy @script)
101+
let transSpine = lookup (mSourceSpine, mTargetSpine) transitions
102+
103+
-- Return
104+
case mOwnInput of
105+
Nothing -> pure $ Initial <$> transSpine
106+
Just _ownInput -> do
107+
-- TODO: fix once Oura has rawCbor for redeemer
108+
-- rdm <- ownInput ^. redeemer
109+
-- pure $ Following $ undefined (rdm ^. redeemerPayload)
110+
pure $ Following <$> transSpine
111+
112+
extractState ::
113+
forall script.
114+
(FromData (CEMScriptDatum script)) =>
115+
TxOutput ->
116+
Maybe (State script)
117+
extractState MkTxOutput {_datum = mDtm} =
118+
case mDtm of
119+
Nothing -> Nothing
120+
Just dtm -> do
121+
let MkDatum _ _ cbor = dtm
122+
let datumAsData :: PlutusLedgerApi.V1.Data =
123+
cbor
124+
& C.toPlutusData
125+
. C.getScriptData
126+
. fromJust
127+
. eitherToMaybe
128+
. C.deserialiseFromCBOR C.AsHashableScriptData
129+
. B16.decodeBase16Lenient -- use base64
130+
. encodeUtf8
131+
let ~(Just (_, _, state)) = PlutusLedgerApi.V1.fromData @(CEMScriptDatum script) datumAsData
132+
pure state
133+
134+
hasAddr :: C.Address C.ShelleyAddr -> TxOutput -> Bool
135+
hasAddr addr' output =
136+
let addr = output ^. address
137+
in fromOuraAddress addr == addr'
138+
139+
fromOuraAddress :: Address -> C.Address C.ShelleyAddr
140+
fromOuraAddress (MkAddressAsBase64 addr) =
141+
addr
142+
& fromJust
143+
. eitherToMaybe
144+
. SerialiseRaw.deserialiseFromRawBytes (C.AsAddress C.AsShelleyAddr)
145+
. B16.decodeBase16Lenient -- use base64
146+
. encodeUtf8

src/Cardano/CEM/Indexing.hs renamed to src/Cardano/CEM/Indexing/Oura.hs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
module Cardano.CEM.Indexing (
1+
{- | CEM provides the building blocks to build an indexer for your dApp.
2+
Current implementation is based on Oura. This module provides tools to
3+
run Oura.
4+
-}
5+
module Cardano.CEM.Indexing.Oura (
26
SourcePath (MkSourcePath, unSourcePath),
37
SinkPath (MkSinkPath, unSinkPath),
48
Filter (MkFilter, unFilter),

0 commit comments

Comments
 (0)