@helios-lang/contract-utils 0.1.20 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/examples/multi-validator/README.md +13 -0
  2. package/examples/multi-validator/contract.js +28 -0
  3. package/examples/{spending → multi-validator}/index.d.ts +3 -3
  4. package/examples/multi-validator/index.js +27 -0
  5. package/examples/multi-validator/minting.hl +5 -0
  6. package/examples/{spending → multi-validator}/package-lock.json +6 -15
  7. package/examples/{spending → multi-validator}/package.json +0 -1
  8. package/examples/multi-validator/spending.hl +18 -0
  9. package/examples/multi-validator/utils.hl +9 -0
  10. package/package.json +7 -6
  11. package/src/{runtime → cast}/Cast.js +31 -8
  12. package/src/cast/index.js +12 -0
  13. package/src/codegen/LoadedModule.js +9 -0
  14. package/src/codegen/{TypescriptWriter.js → LoadedScriptsWriter.js} +149 -108
  15. package/src/codegen/LoadedValidator.js +23 -0
  16. package/src/codegen/{Module.js → TypeCheckedModule.js} +3 -3
  17. package/src/codegen/{Validator.js → TypeCheckedValidator.js} +5 -8
  18. package/src/codegen/TypeSchema.js +6 -2
  19. package/src/codegen/index.js +5 -4
  20. package/src/compiler/CompilerLib.js +51 -0
  21. package/src/{lib/Libg_v0_16.js → compiler/CompilerLib_v0_16.js} +244 -255
  22. package/src/compiler/index.js +6 -0
  23. package/src/compiler/ops.js +137 -0
  24. package/src/context/ContractContext.js +78 -0
  25. package/src/context/ContractContextBuilder.js +69 -0
  26. package/src/context/DagCompiler.js +229 -0
  27. package/src/context/index.js +1 -0
  28. package/src/hl2ts.js +116 -104
  29. package/src/index.js +12 -1
  30. package/types/cast/Cast.d.ts +32 -0
  31. package/types/cast/Cast.d.ts.map +1 -0
  32. package/types/cast/PermissiveType.d.ts +3 -0
  33. package/types/cast/PermissiveType.d.ts.map +1 -0
  34. package/types/cast/StrictType.d.ts +3 -0
  35. package/types/cast/StrictType.d.ts.map +1 -0
  36. package/types/cast/index.d.ts +5 -0
  37. package/types/cast/index.d.ts.map +1 -0
  38. package/types/codegen/LoadedModule.d.ts +6 -0
  39. package/types/codegen/LoadedModule.d.ts.map +1 -0
  40. package/types/codegen/LoadedScriptsWriter.d.ts +57 -0
  41. package/types/codegen/LoadedScriptsWriter.d.ts.map +1 -0
  42. package/types/codegen/LoadedValidator.d.ts +16 -0
  43. package/types/codegen/LoadedValidator.d.ts.map +1 -0
  44. package/types/codegen/TypeCheckedModule.d.ts +11 -0
  45. package/types/codegen/TypeCheckedModule.d.ts.map +1 -0
  46. package/types/codegen/TypeCheckedValidator.d.ts +8 -0
  47. package/types/codegen/TypeCheckedValidator.d.ts.map +1 -0
  48. package/types/codegen/TypeSchema.d.ts +10 -0
  49. package/types/codegen/TypeSchema.d.ts.map +1 -1
  50. package/types/codegen/TypescriptWriter.d.ts +4 -4
  51. package/types/codegen/Validator.d.ts +1 -1
  52. package/types/codegen/index.d.ts +5 -4
  53. package/types/codegen/index.d.ts.map +1 -1
  54. package/types/compiler/CompilerLib.d.ts +45 -0
  55. package/types/compiler/CompilerLib.d.ts.map +1 -0
  56. package/types/compiler/CompilerLib_v0_16.d.ts +271 -0
  57. package/types/compiler/CompilerLib_v0_16.d.ts.map +1 -0
  58. package/types/compiler/index.d.ts +4 -0
  59. package/types/compiler/index.d.ts.map +1 -0
  60. package/types/compiler/ops.d.ts +24 -0
  61. package/types/compiler/ops.d.ts.map +1 -0
  62. package/types/context/ContractContext.d.ts +23 -0
  63. package/types/context/ContractContext.d.ts.map +1 -0
  64. package/types/context/ContractContextBuilder.d.ts +53 -0
  65. package/types/context/ContractContextBuilder.d.ts.map +1 -0
  66. package/types/context/DagCompiler.d.ts +64 -0
  67. package/types/context/DagCompiler.d.ts.map +1 -0
  68. package/types/context/index.d.ts +2 -0
  69. package/types/context/index.d.ts.map +1 -0
  70. package/types/hl2ts.d.ts +9 -4
  71. package/types/hl2ts.d.ts.map +1 -1
  72. package/types/index.d.ts +4 -1
  73. package/types/index.d.ts.map +1 -1
  74. package/types/lib/Lib.d.ts +13 -71
  75. package/types/lib/Lib.d.ts.map +1 -1
  76. package/types/lib/Libg_v0_16.d.ts +53 -52
  77. package/types/lib/Libg_v0_16.d.ts.map +1 -1
  78. package/types/lib/index.d.ts +0 -1
  79. package/types/lib/index.d.ts.map +1 -1
  80. package/types/lib/load.d.ts +1 -7
  81. package/types/lib/load.d.ts.map +1 -1
  82. package/types/runtime/ContractContext.d.ts +22 -43
  83. package/types/runtime/ContractContext.d.ts.map +1 -1
  84. package/types/runtime/ContractContextBuilder.d.ts +15 -123
  85. package/types/runtime/ContractContextBuilder.d.ts.map +1 -1
  86. package/types/runtime/DagCompiler.d.ts +64 -0
  87. package/types/runtime/DagCompiler.d.ts.map +1 -0
  88. package/types/runtime/PermissiveType.d.ts +1 -1
  89. package/types/runtime/StrictType.d.ts +1 -1
  90. package/types/runtime/index.d.ts +0 -2
  91. package/examples/spending/contract.js +0 -16
  92. package/examples/spending/index.js +0 -27
  93. package/examples/spending/minting.hl +0 -5
  94. package/examples/spending/spending.hl +0 -17
  95. package/examples/spending/utils.hl +0 -5
  96. package/src/codegen/Writer.js +0 -37
  97. package/src/lib/Lib.js +0 -56
  98. package/src/lib/index.js +0 -6
  99. package/src/lib/load.js +0 -26
  100. package/src/runtime/ContractContext.js +0 -35
  101. package/src/runtime/ContractContextBuilder.js +0 -340
  102. package/src/runtime/index.js +0 -3
  103. /package/src/{runtime → cast}/PermissiveType.js +0 -0
  104. /package/src/{runtime → cast}/StrictType.js +0 -0
@@ -0,0 +1,137 @@
1
+ import * as unsafeLib from "@helios-lang/compiler"
2
+ import {
3
+ translateImportPaths as translateImportPathsInternal,
4
+ readHeader
5
+ } from "@helios-lang/compiler-utils"
6
+ import { CompilerLib_v0_16 } from "./CompilerLib_v0_16.js"
7
+
8
+ /**
9
+ * @typedef {import("./CompilerLib.js").CompilerLib} CompilerLib
10
+ * @typedef {import("./CompilerLib.js").TypeCheckOutput} TypeCheckOutput
11
+ */
12
+
13
+ /**
14
+ * @param {{VERSION: string}} lib
15
+ * @returns {number[]}
16
+ */
17
+ function getVersion(lib) {
18
+ return lib.VERSION.split(".").map((v) => Number(v))
19
+ }
20
+
21
+ /**
22
+ * The compiler library is passed as an argument instead of loading locally, so any optional configuration of the library can be done externally
23
+ * @returns {CompilerLib}
24
+ */
25
+ export function loadCompilerLib() {
26
+ const [major, minor] = getVersion(unsafeLib)
27
+
28
+ switch (major) {
29
+ case 0:
30
+ switch (minor) {
31
+ case 16:
32
+ return new CompilerLib_v0_16(unsafeLib)
33
+ }
34
+ default:
35
+ throw new Error(
36
+ `compiler version ${unsafeLib.VERSION} not supported`
37
+ )
38
+ }
39
+ }
40
+
41
+ /**
42
+ * The compiler library is passed as an argument instead of loading locally, so any optional configuration of the library can be done externally
43
+ * @param {CompilerLib} lib
44
+ * @param {{[path: string]: string}} files
45
+ * @param {(current: string, rel: string) => string} relToAbs
46
+ * @returns {TypeCheckOutput}
47
+ */
48
+ export function typeCheckFiles(lib, files, relToAbs) {
49
+ const scripts = translateImportPaths(files, relToAbs)
50
+
51
+ return typeCheckScripts(lib, scripts)
52
+ }
53
+
54
+ /**
55
+ * @param {CompilerLib} lib
56
+ * @param {string[]} scripts
57
+ * @returns {TypeCheckOutput}
58
+ */
59
+ export function typeCheckScripts(lib, scripts) {
60
+ const [validators, modules] = splitValidatorsAndModules(scripts)
61
+
62
+ return lib.typeCheck(validators, modules)
63
+ }
64
+
65
+ /**
66
+ * Filters out duplicates
67
+ * @param {string[]} scripts
68
+ * @returns {[string[], string[]]} - [validators, modules]
69
+ */
70
+ function splitValidatorsAndModules(scripts) {
71
+ /**
72
+ * @type {Map<string, string>}
73
+ */
74
+ const validators = new Map()
75
+
76
+ /**
77
+ * @type {Map<string, string>}
78
+ */
79
+ const modules = new Map()
80
+
81
+ scripts.forEach((s) => {
82
+ const [purpose, name] = readHeader(s)
83
+
84
+ switch (purpose) {
85
+ case "module":
86
+ modules.set(name, s)
87
+ break
88
+ case "minting":
89
+ case "spending":
90
+ case "certifying":
91
+ case "rewarding":
92
+ case "staking":
93
+ validators.set(name, s)
94
+ break
95
+ default:
96
+ throw new Error(
97
+ `unhandled script purpose ${purpose} in script ${name}`
98
+ )
99
+ }
100
+ })
101
+
102
+ return [Array.from(validators.values()), Array.from(modules.values())]
103
+ }
104
+
105
+ /**
106
+ *
107
+ * @param {{[path: string]: string}} files
108
+ * @param {(current: string, rel: string) => string} relToAbs
109
+ * @returns {string[]}
110
+ */
111
+ function translateImportPaths(files, relToAbs) {
112
+ const partialSources = {}
113
+
114
+ for (let path in files) {
115
+ const [purpose, name] = readHeader(files[path])
116
+
117
+ partialSources[path] = {
118
+ name: name,
119
+ purpose: purpose,
120
+ sourceCode: files[path]
121
+ }
122
+ }
123
+
124
+ return Object.entries(files).map(([path, file]) =>
125
+ translateImportPathsInternal(file, (relPath) => {
126
+ const absPath = relToAbs(path, relPath)
127
+
128
+ const d = partialSources[absPath] ?? partialSources[absPath + ".hl"]
129
+
130
+ if (!d) {
131
+ throw new Error(`'${relPath}' not found`)
132
+ }
133
+
134
+ return d.name
135
+ })
136
+ )
137
+ }
@@ -0,0 +1,78 @@
1
+ import {
2
+ StakingValidatorHash,
3
+ ValidatorHash,
4
+ MintingPolicyHash
5
+ } from "@helios-lang/ledger"
6
+ import { Cast } from "../cast/index.js"
7
+
8
+ /**
9
+ * @typedef {import("../codegen/index.js").LoadedValidator} LoadedValidator
10
+ */
11
+
12
+ /**
13
+ * @template TRedeemerStrict
14
+ * @template TRedeemerPermissive
15
+ * @typedef {import("@helios-lang/ledger").MintingContext<TRedeemerStrict, TRedeemerPermissive>} MintingContext
16
+ */
17
+
18
+ /**
19
+ * @template TDatumStrict
20
+ * @template TDatumPermissive
21
+ * @template TRedeemerStrict
22
+ * @template TRedeemerPermissive
23
+ * @typedef {import("@helios-lang/ledger").SpendingContext<TDatumStrict, TDatumPermissive, TRedeemerStrict, TRedeemerPermissive>} SpendingContext
24
+ */
25
+
26
+ /**
27
+ * @template TRedeemerStrict
28
+ * @template TRedeemerPermissive
29
+ * @typedef {import("@helios-lang/ledger").StakingContext<TRedeemerStrict, TRedeemerPermissive>} StakingContext
30
+ */
31
+
32
+ /**
33
+ * @template {Cast} C
34
+ * @typedef {import("../cast/index.js").PermissiveType<C>} PermissiveType
35
+ */
36
+
37
+ /**
38
+ * @template {Cast} C
39
+ * @typedef {import("../cast/index.js").StrictType<C>} StrictType
40
+ */
41
+
42
+ /**
43
+ * @typedef {(
44
+ * ValidatorHash<SpendingContext<any, any, any, any>> |
45
+ * MintingPolicyHash<MintingContext<any, any>> |
46
+ * StakingValidatorHash<StakingContext<any, any>>
47
+ * )} AnyContractValidatorHash
48
+ */
49
+
50
+ /**
51
+ * @template {LoadedValidator} V
52
+ * @typedef {V extends {"$purpose": "spending", "$Datum": Cast} ? ValidatorHash<
53
+ * SpendingContext<
54
+ * StrictType<V["$Datum"]>,
55
+ * PermissiveType<V["$Datum"]>,
56
+ * StrictType<V["$Redeemer"]>,
57
+ * PermissiveType<V["$Redeemer"]>
58
+ * >
59
+ * > :
60
+ * V extends {"$purpose": "minting"} ? MintingPolicyHash<
61
+ * MintingContext<
62
+ * StrictType<V["$Redeemer"]>,
63
+ * PermissiveType<V["$Redeemer"]>
64
+ * >
65
+ * > :
66
+ * V extends {"$purpose": ("certifying" | "rewarding")} ? StakingValidatorHash<
67
+ * StakingContext<
68
+ * StrictType<V["$Redeemer"]>,
69
+ * PermissiveType<V["$Redeemer"]>
70
+ * >
71
+ * > : never
72
+ * } ContractValidatorHash
73
+ */
74
+
75
+ /**
76
+ * @template {{[name: string]: LoadedValidator}} T
77
+ * @typedef {{[K in keyof T]: ContractValidatorHash<T[K]>}} ContractContext
78
+ */
@@ -0,0 +1,69 @@
1
+ import { None } from "@helios-lang/type-utils"
2
+ import { loadCompilerLib } from "../compiler/index.js"
3
+ import { DagCompiler } from "./DagCompiler.js"
4
+
5
+ /**
6
+ * @typedef {import("../codegen/LoadedModule.js").LoadedModule} LoadedModule
7
+ * @typedef {import("../codegen/LoadedValidator.js").LoadedValidator} LoadedValidator
8
+ * @typedef {import("../compiler/index.js").CompilerLib} CompilerLib
9
+ */
10
+
11
+ /**
12
+ * @template {{[name: string]: LoadedValidator}} T
13
+ * @typedef {import("./ContractContext.js").ContractContext<T>} ContractContext
14
+ */
15
+
16
+ /**
17
+ * @template {{[name: string]: LoadedValidator}} T
18
+ */
19
+ export class ContractContextBuilder {
20
+ /**
21
+ * @private
22
+ * @readonly
23
+ * @type {T}
24
+ */
25
+ validators
26
+
27
+ /**
28
+ * @private
29
+ * @param {T} validators
30
+ */
31
+ constructor(validators) {
32
+ this.validators = validators
33
+ }
34
+
35
+ /**
36
+ * @returns {ContractContextBuilder<{}>}
37
+ */
38
+ static new() {
39
+ return new ContractContextBuilder({})
40
+ }
41
+
42
+ /**
43
+ * @template {LoadedValidator} V
44
+ * @param {V} validator
45
+ * @returns {ContractContextBuilder<T & {
46
+ * [K in V["$name"]]: V
47
+ * }>}
48
+ */
49
+ with(validator) {
50
+ return new ContractContextBuilder({
51
+ ...this.validators,
52
+ [validator.$name]: validator
53
+ })
54
+ }
55
+
56
+ /**
57
+ * @param {Option<{[name: string]: string}>} expectedHashes
58
+ * @returns {ContractContext<T>}
59
+ */
60
+ build(expectedHashes = None) {
61
+ const lib = loadCompilerLib()
62
+
63
+ const dagCompiler = new DagCompiler(lib)
64
+
65
+ return /** @type {any} */ (
66
+ dagCompiler.build(Object.values(this.validators), expectedHashes)
67
+ )
68
+ }
69
+ }
@@ -0,0 +1,229 @@
1
+ import { bytesToHex } from "@helios-lang/codec-utils"
2
+ import {
3
+ MintingPolicyHash,
4
+ StakingValidatorHash,
5
+ ValidatorHash
6
+ } from "@helios-lang/ledger"
7
+ import { None } from "@helios-lang/type-utils"
8
+ import { UplcProgramV2 } from "@helios-lang/uplc"
9
+
10
+ /**
11
+ * @typedef {import("../codegen/LoadedValidator.js").LoadedValidator} LoadedValidator
12
+ * @typedef {import("../compiler/index.js").CompilerLib} CompilerLib
13
+ * @typedef {import("./ContractContext.js").AnyContractValidatorHash} AnyContractValidatorHash
14
+ */
15
+
16
+ /**
17
+ * Compiles a set of validators that depend on eachothers hashes (forming a Directed Acyclical Graph)
18
+ */
19
+ export class DagCompiler {
20
+ /**
21
+ * @private
22
+ * @readonly
23
+ * @type {CompilerLib}
24
+ */
25
+ lib
26
+
27
+ /**
28
+ * @private
29
+ * @readonly
30
+ * @type {{[name: string]: AnyContractValidatorHash}}
31
+ */
32
+ cache
33
+
34
+ /**
35
+ * @param {CompilerLib} lib
36
+ */
37
+ constructor(lib) {
38
+ this.lib = lib
39
+ this.cache = {}
40
+ }
41
+
42
+ /**
43
+ * @param {LoadedValidator[]} validators
44
+ * @param {Option<{[name: string]: string}>} expectedHashes
45
+ * @returns {{[name: string]: AnyContractValidatorHash}}
46
+ */
47
+ build(validators, expectedHashes = None) {
48
+ const hashTypes = this.getHashTypes(validators)
49
+
50
+ /**
51
+ * `buildValidator()` is a closure instead of a member method of DagCompiler
52
+ * because this way we have easy access to `hashTypes` and `expectedHashes`
53
+ * @param {LoadedValidator} validator
54
+ */
55
+ const buildValidator = (validator) => {
56
+ const name = validator.$name
57
+ const sourceCode = validator.$sourceCode
58
+
59
+ // if already built before, return immediately
60
+ if (name in this.cache) {
61
+ return
62
+ }
63
+
64
+ // make sure all the hash dependencies are built first
65
+ validator.$hashDependencies.forEach((v) => buildValidator(v))
66
+
67
+ // get the dependencies
68
+ const hashDeps = this.getHashDependencies(validator)
69
+ const moduleDeps = getModuleDependencies(validator)
70
+
71
+ // optimized compilation
72
+ const { cborHex: optimizedCborHex, prettyIR } = this.lib.compile(
73
+ sourceCode,
74
+ moduleDeps,
75
+ {
76
+ optimize: true,
77
+ hashDependencies: hashDeps,
78
+ allValidatorHashTypes: hashTypes,
79
+ dependsOnOwnHash: validator.$dependsOnOwnHash
80
+ }
81
+ )
82
+
83
+ const optimizedProgram = UplcProgramV2.fromCbor(optimizedCborHex)
84
+
85
+ // calculate the hash
86
+ const ownHash = bytesToHex(optimizedProgram.hash())
87
+
88
+ // optionally assert the own hash is equal to a given hash
89
+ if (expectedHashes && name in expectedHashes) {
90
+ if (expectedHashes[name] != ownHash) {
91
+ console.log(prettyIR)
92
+
93
+ throw new Error(
94
+ `expected hash ${expectedHashes[name]} for validator ${name}, got ${ownHash}`
95
+ )
96
+ }
97
+ }
98
+
99
+ // unoptimized compilation (so traces are untouched)
100
+ const { cborHex: unoptimizedCborHex } = this.lib.compile(
101
+ validator.$sourceCode,
102
+ Array.from(moduleDeps.values()),
103
+ {
104
+ optimize: false,
105
+ hashDependencies: hashDeps,
106
+ allValidatorHashTypes: hashTypes,
107
+ ownHash: validator.$dependsOnOwnHash ? ownHash : undefined
108
+ }
109
+ )
110
+
111
+ const unoptimizedProgram =
112
+ UplcProgramV2.fromCbor(unoptimizedCborHex)
113
+
114
+ // add result to cache (unoptimizedProgram is attached to optimizedProgram)
115
+ this.addToCache(
116
+ validator,
117
+ optimizedProgram.withAlt(unoptimizedProgram),
118
+ ownHash
119
+ )
120
+ }
121
+
122
+ validators.forEach((v) => buildValidator(v))
123
+
124
+ return this.cache
125
+ }
126
+
127
+ /**
128
+ * @private
129
+ * @param {LoadedValidator} validator
130
+ * @param {UplcProgramV2} program
131
+ * @param {string} hash - although this can be derived from `program`, reuse this to save some effort
132
+ */
133
+ addToCache(validator, program, hash) {
134
+ const name = validator.$name
135
+ const redeemer = validator.$Redeemer
136
+
137
+ switch (validator.$purpose) {
138
+ case "spending":
139
+ this.cache[name] = new ValidatorHash(hash, {
140
+ program,
141
+ redeemer,
142
+ datum: validator.$Datum
143
+ })
144
+ break
145
+ case "minting":
146
+ this.cache[name] = new MintingPolicyHash(hash, {
147
+ program,
148
+ redeemer
149
+ })
150
+ break
151
+ case "certifying":
152
+ case "rewarding":
153
+ case "staking":
154
+ this.cache[name] = new StakingValidatorHash(hash, {
155
+ program,
156
+ redeemer
157
+ })
158
+ break
159
+ default:
160
+ throw new Error("unhandled purpose")
161
+ }
162
+ }
163
+
164
+ /**
165
+ * @private
166
+ * @param {LoadedValidator} validator
167
+ * @returns {{[name: string]: string}} - TODO: do we really need the purpose?
168
+ */
169
+ getHashDependencies(validator) {
170
+ /**
171
+ * @type {{[name: string]: string}}
172
+ */
173
+ const deps = {}
174
+
175
+ validator.$hashDependencies.forEach((dep) => {
176
+ const k = dep.$name
177
+ const v = this.cache[k]
178
+
179
+ if (!v) {
180
+ throw new Error(
181
+ `${k} should've been built before ${validator.$name}`
182
+ )
183
+ }
184
+
185
+ deps[k] = v.toHex()
186
+ })
187
+
188
+ return deps
189
+ }
190
+
191
+ /**
192
+ * @private
193
+ * @param {LoadedValidator[]} validators
194
+ * @returns {{[name: string]: any}}
195
+ */
196
+ getHashTypes(validators) {
197
+ return Object.fromEntries(
198
+ validators.map((v) => {
199
+ return [v.$name, this.lib.getScriptHashType(v.$purpose)]
200
+ })
201
+ )
202
+ }
203
+ }
204
+
205
+ /**
206
+ * @param {LoadedValidator} validator
207
+ * @returns {string[]}
208
+ */
209
+ export function getModuleDependencies(validator) {
210
+ /**
211
+ * @type {Map<string, string>}
212
+ */
213
+ const deps = new Map()
214
+
215
+ let stack = validator.$dependencies.slice()
216
+ let m = stack.pop()
217
+
218
+ while (m) {
219
+ if (!deps.has(m.$name)) {
220
+ deps.set(m.$name, m.$sourceCode)
221
+
222
+ stack = stack.concat(m.$dependencies)
223
+ }
224
+
225
+ m = stack.pop()
226
+ }
227
+
228
+ return Array.from(deps.values())
229
+ }
@@ -0,0 +1 @@
1
+ export { ContractContextBuilder } from "./ContractContextBuilder.js"