Skip to content

Commit 56e0834

Browse files
authored
PlutusLedgerApi.Envelope module and tests for JSON envelope generation (#7197)
* Add Envelope module and tests for JSON envelope generation * Changelog * create-json-envelope executable uses `writeCodeEnvelop` getting rid of the `cardano-api` dependency * Add support for multiple Plutus versions in JSON envelope functions
1 parent 589516f commit 56e0834

File tree

9 files changed

+198
-33
lines changed

9 files changed

+198
-33
lines changed

cabal.project

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,3 @@ allow-newer:
8585
, inline-r:containers
8686
, inline-r:primitive
8787

88-
-- Uncomment the following lines to make cardano-constitution:create-json-envelope buildable:True
89-
--
90-
-- package cardano-constitution
91-
-- flags: +force-build
92-
-- allow-newer: *:plutus-ledger-api
93-
-- allow-older: *:nothunks
Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,20 @@
1-
-- editorconfig-checker-disable-file
1+
{-# LANGUAGE LambdaCase #-}
22
{-# LANGUAGE OverloadedStrings #-}
3+
34
module Main where
45

5-
import Cardano.Api (File (..), PlutusScriptV3, PlutusScriptVersion (PlutusScriptV3),
6-
Script (PlutusScript), writeFileTextEnvelope)
7-
import Cardano.Api.Shelley (PlutusScript (PlutusScriptSerialised))
86
import Cardano.Constitution.Validator.Sorted (defaultConstitutionCode)
9-
import PlutusLedgerApi.Common (serialiseCompiledCode)
7+
import PlutusLedgerApi.Envelope (writeCodeEnvelope)
108
import System.Environment (getArgs)
11-
import System.Exit
9+
import System.Exit (die)
1210

1311
main :: IO ()
14-
main = do
15-
args <- getArgs
16-
case args of
17-
[file] -> either (error . show) pure
18-
=<< writeFileTextEnvelope (File file) (Just "*BE CAREFUL* that this is compiled from a release commit of plutus and not from master") compiledScript
19-
_ -> die "USAGE: create-json-envelope OUT_FILE"
20-
21-
compiledScript :: Script PlutusScriptV3
22-
compiledScript =
23-
PlutusScript PlutusScriptV3
24-
. PlutusScriptSerialised
25-
. serialiseCompiledCode
26-
$ defaultConstitutionCode
12+
main =
13+
getArgs >>= \case
14+
[file] ->
15+
writeCodeEnvelope
16+
"*BE CAREFUL* that this is compiled from a release commit \
17+
\of Plutus and not from master"
18+
defaultConstitutionCode
19+
file
20+
_ -> die "USAGE: create-json-envelope OUT_FILE"

cardano-constitution/cardano-constitution.cabal

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -147,17 +147,9 @@ test-suite cardano-constitution-test
147147

148148
executable create-json-envelope
149149
import: lang, ghc-version-support, os-support
150-
151-
-- This is a temporary workaround to solve the plutus-ledger-api dependency conflict
152-
-- caused by the `cardano-api` dependency.
153-
-- To force-build it, uncomment the relevant lines in `cabal.project`
154-
if !flag(force-build)
155-
buildable: False
156-
157150
hs-source-dirs: app/create-json-envelope
158151
main-is: Main.hs
159152
build-depends:
160153
, base
161-
, cardano-api
162154
, cardano-constitution
163155
, plutus-ledger-api ^>=1.49
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
### Added
2+
3+
- `PlutusLedgerApi.Envelope` module with two functions:
4+
- `compiledCodeEnvelope`: creates a JSON envelope for `CompiledCode` with a description.
5+
- `writeCodeEnvelope`: writes a JSON envelope for `CompiledCode` to a file.

plutus-ledger-api/plutus-ledger-api.cabal

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ library
6262
PlutusLedgerApi.Data.V1
6363
PlutusLedgerApi.Data.V2
6464
PlutusLedgerApi.Data.V3
65+
PlutusLedgerApi.Envelope
6566
PlutusLedgerApi.MachineParameters
6667
PlutusLedgerApi.V1
6768
PlutusLedgerApi.V1.Address
@@ -110,6 +111,8 @@ library
110111
Prettyprinter.Extras
111112

112113
build-depends:
114+
, aeson
115+
, aeson-pretty
113116
, base >=4.9 && <5
114117
, base16-bytestring >=1
115118
, bytestring
@@ -222,6 +225,7 @@ test-suite plutus-ledger-api-plugin-test
222225
Spec.Data.MintValue.V3
223226
Spec.Data.ScriptContext
224227
Spec.Data.Value
228+
Spec.Envelope
225229
Spec.MintValue.V3
226230
Spec.ReturnUnit.V1
227231
Spec.ReturnUnit.V2
@@ -237,6 +241,7 @@ test-suite plutus-ledger-api-plugin-test
237241
, base >=4.9 && <5
238242
, bytestring
239243
, containers
244+
, filepath
240245
, lens
241246
, mtl
242247
, plutus-core ^>=1.49
@@ -249,6 +254,7 @@ test-suite plutus-ledger-api-plugin-test
249254
, prettyprinter
250255
, QuickCheck
251256
, tasty
257+
, tasty-golden
252258
, tasty-hunit
253259
, tasty-quickcheck
254260

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
3+
module PlutusLedgerApi.Envelope (
4+
compiledCodeEnvelope,
5+
compiledCodeEnvelopeForVersion,
6+
writeCodeEnvelope,
7+
writeCodeEnvelopeForVersion,
8+
) where
9+
10+
import Data.Aeson ((.=))
11+
import Data.Aeson qualified as Json
12+
import Data.Aeson.Encode.Pretty qualified as Json
13+
import Data.ByteString.Base16 qualified as Base16
14+
import Data.ByteString.Lazy qualified as LBS
15+
import Data.ByteString.Short qualified as BS
16+
import Data.Text (Text)
17+
import Data.Text.Encoding (decodeUtf8)
18+
import PlutusLedgerApi.Common.SerialisedScript (serialiseCompiledCode)
19+
import PlutusLedgerApi.Common.Versions (PlutusLedgerLanguage (..))
20+
import PlutusTx.Code (CompiledCode)
21+
22+
{-| Produce a JSON envelope containing 'CompiledCode' serialised with
23+
CBOR and encoded in Base 16 (aka. HEX), using PlutusV3 by default.
24+
25+
"Envelope" is a JSON object with the following fields:
26+
@
27+
{
28+
"type": "PlutusScriptV3",
29+
"description": "A description of the code",
30+
"cborHex": "..."
31+
}
32+
@
33+
-}
34+
compiledCodeEnvelope
35+
:: Text
36+
-- ^ Description of the code
37+
-> CompiledCode a
38+
-- ^ Compiled code to wrap in the envelope
39+
-> Json.Value
40+
-- ^ JSON envelope
41+
compiledCodeEnvelope = compiledCodeEnvelopeForVersion PlutusV3
42+
43+
{-| Produce a JSON envelope containing 'CompiledCode' serialised with
44+
CBOR and encoded in Base 16 (aka. HEX).
45+
46+
"Envelope" is a JSON object with the following fields:
47+
@
48+
{
49+
"type": "PlutusScriptV2",
50+
"description": "A description of the code",
51+
"cborHex": "..."
52+
}
53+
@
54+
-}
55+
compiledCodeEnvelopeForVersion
56+
:: PlutusLedgerLanguage
57+
-- ^ Language of the compiled code, e.g. 'PlutusLedgerLanguage.PlutusV3'
58+
-> Text
59+
-- ^ Description of the code
60+
-> CompiledCode a
61+
-- ^ Compiled code to wrap in the envelope
62+
-> Json.Value
63+
-- ^ JSON envelope
64+
compiledCodeEnvelopeForVersion lang desc code =
65+
Json.object
66+
[ "type" .= typ
67+
, "description" .= desc
68+
, "cborHex" .= hex
69+
]
70+
where
71+
typ :: Text =
72+
case lang of
73+
PlutusV1 -> "PlutusScriptV1"
74+
PlutusV2 -> "PlutusScriptV2"
75+
PlutusV3 -> "PlutusScriptV3"
76+
77+
hex = decodeUtf8 (Base16.encode (BS.fromShort (serialiseCompiledCode code)))
78+
79+
{-|
80+
Write a JSON envelope containing 'CompiledCode' serialised with
81+
CBOR and encoded in Base 16 (aka. HEX) to a file on disk, using PlutusV3 by default.
82+
83+
"Envelope" is a JSON object with the following fields:
84+
@
85+
{
86+
"type": "PlutusScriptV3",
87+
"description": "A description of the code",
88+
"cborHex": "..."
89+
}
90+
@
91+
-}
92+
writeCodeEnvelope
93+
:: Text
94+
-- ^ Description of the code
95+
-> CompiledCode a
96+
-- ^ Compiled code to wrap in the envelope
97+
-> FilePath
98+
-- ^ File path to write the envelope to
99+
-> IO ()
100+
writeCodeEnvelope = writeCodeEnvelopeForVersion PlutusV3
101+
102+
{-|
103+
Write a JSON envelope containing 'CompiledCode' serialised with
104+
CBOR and encoded in Base 16 (aka. HEX) to a file on disk.
105+
106+
"Envelope" is a JSON object with the following fields:
107+
@
108+
{
109+
"type": "PlutusScriptV2",
110+
"description": "A description of the code",
111+
"cborHex": "..."
112+
}
113+
@
114+
-}
115+
writeCodeEnvelopeForVersion
116+
:: PlutusLedgerLanguage
117+
-- ^ Language of the compiled code, e.g. 'PlutusLedgerLanguage.PlutusV3'
118+
-> Text
119+
-- ^ Description of the code
120+
-> CompiledCode a
121+
-- ^ Compiled code to wrap in the envelope
122+
-> FilePath
123+
-- ^ File path to write the envelope to
124+
-> IO ()
125+
writeCodeEnvelopeForVersion lang desc code path = do
126+
let envelope = compiledCodeEnvelopeForVersion lang desc code
127+
-- aeson-pretty doesn't add a newline at the end, so we add it manually
128+
envelopePretty = Json.encodePretty' config envelope <> "\n"
129+
LBS.writeFile path envelopePretty
130+
where
131+
config =
132+
Json.defConfig
133+
{ Json.confCompare =
134+
Json.keyOrder ["type", "description", "cborHex"]
135+
}

plutus-ledger-api/test-plugin/Spec.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Spec.Data.Budget qualified
55
import Spec.Data.MintValue.V3 qualified
66
import Spec.Data.ScriptContext qualified
77
import Spec.Data.Value qualified
8+
import Spec.Envelope qualified
89
import Spec.MintValue.V3 qualified
910
import Spec.ReturnUnit.V1 qualified
1011
import Spec.ReturnUnit.V2 qualified
@@ -27,6 +28,7 @@ tests =
2728
, Spec.Data.ScriptContext.tests
2829
, Spec.Data.Value.test_EqValue
2930
, Spec.Data.MintValue.V3.tests
31+
, Spec.Envelope.tests
3032
, Spec.ReturnUnit.V1.tests
3133
, Spec.ReturnUnit.V2.tests
3234
, Spec.ReturnUnit.V3.tests
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{-# LANGUAGE DataKinds #-}
2+
{-# LANGUAGE OverloadedStrings #-}
3+
{-# LANGUAGE TemplateHaskell #-}
4+
5+
module Spec.Envelope where
6+
7+
import PlutusLedgerApi.Envelope (writeCodeEnvelope)
8+
import PlutusTx qualified
9+
import PlutusTx.Prelude qualified as P
10+
import System.FilePath ((</>))
11+
import Test.Tasty (TestTree, testGroup)
12+
import Test.Tasty.Golden (goldenVsFile)
13+
14+
tests :: TestTree
15+
tests = testGroup "Envelope" [testTrivialEnvelope]
16+
17+
testTrivialEnvelope :: TestTree
18+
testTrivialEnvelope =
19+
goldenVsFile "writeCodeEnvelope produces expected JSON" golden actual $
20+
writeCodeEnvelope
21+
"A trivial function that computes 2 + 2"
22+
$$(PlutusTx.compile [||(2 :: Integer) P.+ 2||])
23+
actual
24+
where
25+
basePath :: FilePath
26+
basePath = "test-plugin/golden"
27+
28+
actual :: FilePath
29+
actual = basePath </> "envelope.actual.json"
30+
31+
golden :: FilePath
32+
golden = basePath </> "envelope.json"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "PlutusScriptV3",
3+
"description": "A trivial function that computes 2 + 2",
4+
"cborHex": "46010100480201"
5+
}

0 commit comments

Comments
 (0)