@proto-kit/module 0.1.1-develop.1086
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.
- package/LICENSE.md +201 -0
- package/README.md +114 -0
- package/dist/factories/MethodIdFactory.d.ts +10 -0
- package/dist/factories/MethodIdFactory.d.ts.map +1 -0
- package/dist/factories/MethodIdFactory.js +10 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/method/MethodParameterEncoder.d.ts +24 -0
- package/dist/method/MethodParameterEncoder.d.ts.map +1 -0
- package/dist/method/MethodParameterEncoder.js +164 -0
- package/dist/method/runtimeMethod.d.ts +33 -0
- package/dist/method/runtimeMethod.d.ts.map +1 -0
- package/dist/method/runtimeMethod.js +167 -0
- package/dist/module/decorator.d.ts +8 -0
- package/dist/module/decorator.d.ts.map +1 -0
- package/dist/module/decorator.js +15 -0
- package/dist/runtime/MethodIdResolver.d.ts +20 -0
- package/dist/runtime/MethodIdResolver.d.ts.map +1 -0
- package/dist/runtime/MethodIdResolver.js +90 -0
- package/dist/runtime/Runtime.d.ts +71 -0
- package/dist/runtime/Runtime.d.ts.map +1 -0
- package/dist/runtime/Runtime.js +220 -0
- package/dist/runtime/RuntimeEnvironment.d.ts +10 -0
- package/dist/runtime/RuntimeEnvironment.d.ts.map +1 -0
- package/dist/runtime/RuntimeEnvironment.js +1 -0
- package/dist/runtime/RuntimeModule.d.ts +37 -0
- package/dist/runtime/RuntimeModule.d.ts.map +1 -0
- package/dist/runtime/RuntimeModule.js +79 -0
- package/dist/state/InMemoryStateService.d.ts +15 -0
- package/dist/state/InMemoryStateService.d.ts.map +1 -0
- package/dist/state/InMemoryStateService.js +23 -0
- package/dist/state/decorator.d.ts +7 -0
- package/dist/state/decorator.d.ts.map +1 -0
- package/dist/state/decorator.js +39 -0
- package/jest.config.cjs +1 -0
- package/package.json +35 -0
- package/src/factories/MethodIdFactory.ts +13 -0
- package/src/index.ts +10 -0
- package/src/method/MethodParameterEncoder.ts +252 -0
- package/src/method/runtimeMethod.ts +307 -0
- package/src/module/decorator.ts +21 -0
- package/src/runtime/MethodIdResolver.ts +108 -0
- package/src/runtime/Runtime.ts +379 -0
- package/src/runtime/RuntimeEnvironment.ts +16 -0
- package/src/runtime/RuntimeModule.ts +112 -0
- package/src/state/InMemoryStateService.ts +25 -0
- package/src/state/decorator.ts +61 -0
- package/test/Runtime.test.ts +70 -0
- package/test/TestingRuntime.ts +45 -0
- package/test/method/MethodParameterEncoder.test.ts +152 -0
- package/test/method/runtimeMethod.test.ts +46 -0
- package/test/modules/Admin.ts +19 -0
- package/test/modules/Balances.test.ts +337 -0
- package/test/modules/Balances.ts +54 -0
- package/test/modules/MethodIdResolver.test.ts +73 -0
- package/test/modules/State.test.ts +81 -0
- package/test/runtimeMethod.test.ts +215 -0
- package/test/tsconfig.json +7 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-argument */
|
|
2
|
+
import { ZkProgram } from "o1js";
|
|
3
|
+
import { DependencyContainer, injectable } from "tsyringe";
|
|
4
|
+
import {
|
|
5
|
+
StringKeyOf,
|
|
6
|
+
ModuleContainer,
|
|
7
|
+
ModulesConfig,
|
|
8
|
+
ModulesRecord,
|
|
9
|
+
TypedClass,
|
|
10
|
+
ZkProgrammable,
|
|
11
|
+
PlainZkProgram,
|
|
12
|
+
AreProofsEnabled,
|
|
13
|
+
ChildContainerProvider,
|
|
14
|
+
} from "@proto-kit/common";
|
|
15
|
+
import {
|
|
16
|
+
MethodPublicOutput,
|
|
17
|
+
StateServiceProvider,
|
|
18
|
+
SimpleAsyncStateService,
|
|
19
|
+
} from "@proto-kit/protocol";
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
combineMethodName,
|
|
23
|
+
isRuntimeMethod,
|
|
24
|
+
runtimeMethodTypeMetadataKey,
|
|
25
|
+
toWrappedMethod,
|
|
26
|
+
AsyncWrappedMethod,
|
|
27
|
+
} from "../method/runtimeMethod";
|
|
28
|
+
import { MethodIdFactory } from "../factories/MethodIdFactory";
|
|
29
|
+
|
|
30
|
+
import { RuntimeModule } from "./RuntimeModule";
|
|
31
|
+
import { MethodIdResolver } from "./MethodIdResolver";
|
|
32
|
+
import { RuntimeEnvironment } from "./RuntimeEnvironment";
|
|
33
|
+
|
|
34
|
+
export function getAllPropertyNames(obj: any) {
|
|
35
|
+
let currentPrototype: any | undefined = obj;
|
|
36
|
+
let keys: (string | symbol)[] = [];
|
|
37
|
+
// if primitive (primitives still have keys) skip the first iteration
|
|
38
|
+
if (!(obj instanceof Object)) {
|
|
39
|
+
currentPrototype = Object.getPrototypeOf(obj);
|
|
40
|
+
}
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
42
|
+
while (currentPrototype) {
|
|
43
|
+
keys = keys.concat(Reflect.ownKeys(currentPrototype));
|
|
44
|
+
currentPrototype = Object.getPrototypeOf(currentPrototype);
|
|
45
|
+
}
|
|
46
|
+
return keys;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Record of modules accepted by the Runtime module container.
|
|
51
|
+
*
|
|
52
|
+
* We have to use TypedClass since RuntimeModule
|
|
53
|
+
* is an abstract class
|
|
54
|
+
*/
|
|
55
|
+
export type RuntimeModulesRecord = ModulesRecord<
|
|
56
|
+
TypedClass<RuntimeModule<unknown>>
|
|
57
|
+
>;
|
|
58
|
+
|
|
59
|
+
const errors = {
|
|
60
|
+
methodNotFound: (methodKey: string) =>
|
|
61
|
+
new Error(`Unable to find method with id ${methodKey}`),
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Definition / required arguments for the Runtime class
|
|
66
|
+
*/
|
|
67
|
+
export interface RuntimeDefinition<Modules extends RuntimeModulesRecord> {
|
|
68
|
+
modules: Modules;
|
|
69
|
+
config?: ModulesConfig<Modules>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class RuntimeZkProgrammable<
|
|
73
|
+
Modules extends RuntimeModulesRecord,
|
|
74
|
+
> extends ZkProgrammable<undefined, MethodPublicOutput> {
|
|
75
|
+
public constructor(public runtime: Runtime<Modules>) {
|
|
76
|
+
super();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public get appChain() {
|
|
80
|
+
return this.runtime.appChain;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public zkProgramFactory(): PlainZkProgram<undefined, MethodPublicOutput>[] {
|
|
84
|
+
type Methods = Record<
|
|
85
|
+
string,
|
|
86
|
+
{
|
|
87
|
+
privateInputs: any;
|
|
88
|
+
method: AsyncWrappedMethod;
|
|
89
|
+
}
|
|
90
|
+
>;
|
|
91
|
+
// We need to use explicit type annotations here,
|
|
92
|
+
// therefore we can't use destructuring
|
|
93
|
+
|
|
94
|
+
// eslint-disable-next-line prefer-destructuring
|
|
95
|
+
const runtime: Runtime<Modules> = this.runtime;
|
|
96
|
+
|
|
97
|
+
const MAXIMUM_METHODS_PER_ZK_PROGRAM = 8;
|
|
98
|
+
|
|
99
|
+
const runtimeMethods = runtime.runtimeModuleNames.reduce<Methods>(
|
|
100
|
+
(allMethods, runtimeModuleName) => {
|
|
101
|
+
runtime.isValidModuleName(
|
|
102
|
+
runtime.definition.modules,
|
|
103
|
+
runtimeModuleName
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Couldnt find a better way to circumvent the type assertion
|
|
108
|
+
* regarding resolving only known modules. We assert in the line above
|
|
109
|
+
* but we cast it to any anyways to satisfy the proof system.
|
|
110
|
+
*/
|
|
111
|
+
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
113
|
+
const runtimeModule = runtime.resolve(runtimeModuleName as any);
|
|
114
|
+
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
116
|
+
const modulePrototype = Object.getPrototypeOf(runtimeModule) as Record<
|
|
117
|
+
string,
|
|
118
|
+
// Technically not all methods have to be async, but for this context it's ok
|
|
119
|
+
(...args: unknown[]) => Promise<unknown>
|
|
120
|
+
>;
|
|
121
|
+
|
|
122
|
+
const modulePrototypeMethods = getAllPropertyNames(runtimeModule).map(
|
|
123
|
+
(method) => method.toString()
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const moduleMethods = modulePrototypeMethods.reduce<Methods>(
|
|
127
|
+
(allModuleMethods, methodName) => {
|
|
128
|
+
if (isRuntimeMethod(runtimeModule, methodName)) {
|
|
129
|
+
const combinedMethodName = combineMethodName(
|
|
130
|
+
runtimeModuleName,
|
|
131
|
+
methodName
|
|
132
|
+
);
|
|
133
|
+
const method = modulePrototype[methodName];
|
|
134
|
+
const invocationType = Reflect.getMetadata(
|
|
135
|
+
runtimeMethodTypeMetadataKey,
|
|
136
|
+
runtimeModule,
|
|
137
|
+
methodName
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const wrappedMethod: AsyncWrappedMethod = Reflect.apply(
|
|
141
|
+
toWrappedMethod,
|
|
142
|
+
runtimeModule,
|
|
143
|
+
[methodName, method, { invocationType }]
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const privateInputs = Reflect.getMetadata(
|
|
147
|
+
"design:paramtypes",
|
|
148
|
+
runtimeModule,
|
|
149
|
+
methodName
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
...allModuleMethods,
|
|
154
|
+
|
|
155
|
+
[combinedMethodName]: {
|
|
156
|
+
privateInputs,
|
|
157
|
+
method: wrappedMethod,
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return allModuleMethods;
|
|
163
|
+
},
|
|
164
|
+
{}
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
...allMethods,
|
|
169
|
+
...moduleMethods,
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
{}
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
const sortedRuntimeMethods = Object.fromEntries(
|
|
176
|
+
Object.entries(runtimeMethods).sort()
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const splitRuntimeMethods = () => {
|
|
180
|
+
const buckets: Array<
|
|
181
|
+
Record<
|
|
182
|
+
string,
|
|
183
|
+
{
|
|
184
|
+
privateInputs: any;
|
|
185
|
+
method: AsyncWrappedMethod;
|
|
186
|
+
}
|
|
187
|
+
>
|
|
188
|
+
> = [];
|
|
189
|
+
Object.entries(sortedRuntimeMethods).forEach(
|
|
190
|
+
async ([methodName, method]) => {
|
|
191
|
+
let methodAdded = false;
|
|
192
|
+
for (const bucket of buckets) {
|
|
193
|
+
if (buckets.length === 0) {
|
|
194
|
+
const record: Record<
|
|
195
|
+
string,
|
|
196
|
+
{
|
|
197
|
+
privateInputs: any;
|
|
198
|
+
method: AsyncWrappedMethod;
|
|
199
|
+
}
|
|
200
|
+
> = {};
|
|
201
|
+
record[methodName] = method;
|
|
202
|
+
buckets.push(record);
|
|
203
|
+
methodAdded = true;
|
|
204
|
+
break;
|
|
205
|
+
} else if (
|
|
206
|
+
Object.keys(bucket).length <=
|
|
207
|
+
MAXIMUM_METHODS_PER_ZK_PROGRAM - 1
|
|
208
|
+
) {
|
|
209
|
+
bucket[methodName] = method;
|
|
210
|
+
methodAdded = true;
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (!methodAdded) {
|
|
215
|
+
const record: Record<
|
|
216
|
+
string,
|
|
217
|
+
{
|
|
218
|
+
privateInputs: any;
|
|
219
|
+
method: AsyncWrappedMethod;
|
|
220
|
+
}
|
|
221
|
+
> = {};
|
|
222
|
+
record[methodName] = method;
|
|
223
|
+
buckets.push(record);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
return buckets;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
return splitRuntimeMethods().map((bucket) => {
|
|
231
|
+
const program = ZkProgram({
|
|
232
|
+
name: "RuntimeProgram",
|
|
233
|
+
publicOutput: MethodPublicOutput,
|
|
234
|
+
methods: bucket,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const SelfProof = ZkProgram.Proof(program);
|
|
238
|
+
|
|
239
|
+
const methods = Object.keys(bucket).reduce<Record<string, any>>(
|
|
240
|
+
(boundMethods, methodName) => {
|
|
241
|
+
boundMethods[methodName] = program[methodName].bind(program);
|
|
242
|
+
return boundMethods;
|
|
243
|
+
},
|
|
244
|
+
{}
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
compile: program.compile.bind(program),
|
|
249
|
+
verify: program.verify.bind(program),
|
|
250
|
+
analyzeMethods: program.analyzeMethods.bind(program),
|
|
251
|
+
Proof: SelfProof,
|
|
252
|
+
methods,
|
|
253
|
+
};
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Wrapper for an application specific runtime, which helps orchestrate
|
|
260
|
+
* runtime modules into an interoperable runtime.
|
|
261
|
+
*/
|
|
262
|
+
@injectable()
|
|
263
|
+
export class Runtime<Modules extends RuntimeModulesRecord>
|
|
264
|
+
extends ModuleContainer<Modules>
|
|
265
|
+
implements RuntimeEnvironment
|
|
266
|
+
{
|
|
267
|
+
public static from<Modules extends RuntimeModulesRecord>(
|
|
268
|
+
definition: RuntimeDefinition<Modules>
|
|
269
|
+
): TypedClass<Runtime<Modules>> {
|
|
270
|
+
return class RuntimeScoped extends Runtime<Modules> {
|
|
271
|
+
public constructor() {
|
|
272
|
+
super(definition);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// runtime modules composed into a ZkProgram
|
|
278
|
+
public program?: ReturnType<typeof ZkProgram>;
|
|
279
|
+
|
|
280
|
+
public definition: RuntimeDefinition<Modules>;
|
|
281
|
+
|
|
282
|
+
public zkProgrammable: ZkProgrammable<undefined, MethodPublicOutput>;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Creates a new Runtime from the provided config
|
|
286
|
+
*
|
|
287
|
+
* @param modules - Configuration object for the constructed Runtime
|
|
288
|
+
*/
|
|
289
|
+
public constructor(definition: RuntimeDefinition<Modules>) {
|
|
290
|
+
super(definition);
|
|
291
|
+
this.definition = definition;
|
|
292
|
+
this.zkProgrammable = new RuntimeZkProgrammable<Modules>(this);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// TODO Remove after changing DFs to type-based approach
|
|
296
|
+
public create(childContainerProvider: ChildContainerProvider) {
|
|
297
|
+
super.create(childContainerProvider);
|
|
298
|
+
|
|
299
|
+
this.useDependencyFactory(this.container.resolve(MethodIdFactory));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
public get appChain(): AreProofsEnabled | undefined {
|
|
303
|
+
return this.container.resolve<AreProofsEnabled>("AreProofsEnabled");
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
public get stateServiceProvider(): StateServiceProvider {
|
|
307
|
+
return this.dependencyContainer.resolve<StateServiceProvider>(
|
|
308
|
+
"StateServiceProvider"
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
public get stateService(): SimpleAsyncStateService {
|
|
313
|
+
return this.stateServiceProvider.stateService;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
public get methodIdResolver(): MethodIdResolver {
|
|
317
|
+
return this.container.resolve<MethodIdResolver>("MethodIdResolver");
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* @returns The dependency injection container of this runtime
|
|
322
|
+
*/
|
|
323
|
+
public get dependencyContainer(): DependencyContainer {
|
|
324
|
+
return this.container;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* @param methodId The encoded name of the method to call.
|
|
329
|
+
* Encoding: "stringToField(module.name) << 128 + stringToField(method-name)"
|
|
330
|
+
*/
|
|
331
|
+
public getMethodById(
|
|
332
|
+
methodId: bigint
|
|
333
|
+
): ((...args: unknown[]) => Promise<unknown>) | undefined {
|
|
334
|
+
const methodDescriptor =
|
|
335
|
+
this.methodIdResolver.getMethodNameFromId(methodId);
|
|
336
|
+
|
|
337
|
+
if (methodDescriptor === undefined) {
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
const [moduleName, methodName] = methodDescriptor;
|
|
341
|
+
|
|
342
|
+
this.assertIsValidModuleName(moduleName);
|
|
343
|
+
const module = this.resolve(moduleName);
|
|
344
|
+
|
|
345
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
346
|
+
const method = (module as any)[methodName];
|
|
347
|
+
if (method === undefined) {
|
|
348
|
+
throw errors.methodNotFound(`${moduleName}.${methodName}`);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
352
|
+
return (method as (...args: unknown[]) => Promise<unknown>).bind(module);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Add a name and other respective properties required by RuntimeModules,
|
|
357
|
+
* that come from the current Runtime
|
|
358
|
+
*
|
|
359
|
+
* @param moduleName - Name of the runtime module to decorate
|
|
360
|
+
* @param containedModule
|
|
361
|
+
*/
|
|
362
|
+
public decorateModule(
|
|
363
|
+
moduleName: StringKeyOf<Modules>,
|
|
364
|
+
containedModule: InstanceType<Modules[StringKeyOf<Modules>]>
|
|
365
|
+
) {
|
|
366
|
+
containedModule.name = moduleName;
|
|
367
|
+
containedModule.runtime = this;
|
|
368
|
+
|
|
369
|
+
super.decorateModule(moduleName, containedModule);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* @returns A list of names of all the registered module names
|
|
374
|
+
*/
|
|
375
|
+
public get runtimeModuleNames() {
|
|
376
|
+
return Object.keys(this.definition.modules);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
/* eslint-enable @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-argument */
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AreProofsEnabled, WithZkProgrammable } from "@proto-kit/common";
|
|
2
|
+
import {
|
|
3
|
+
MethodPublicOutput,
|
|
4
|
+
SimpleAsyncStateService,
|
|
5
|
+
StateServiceProvider,
|
|
6
|
+
} from "@proto-kit/protocol";
|
|
7
|
+
|
|
8
|
+
import { MethodIdResolver } from "./MethodIdResolver";
|
|
9
|
+
|
|
10
|
+
export interface RuntimeEnvironment
|
|
11
|
+
extends WithZkProgrammable<undefined, MethodPublicOutput> {
|
|
12
|
+
get appChain(): AreProofsEnabled | undefined;
|
|
13
|
+
get stateService(): SimpleAsyncStateService;
|
|
14
|
+
get stateServiceProvider(): StateServiceProvider;
|
|
15
|
+
get methodIdResolver(): MethodIdResolver;
|
|
16
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { ConfigurableModule, NoConfig, Presets } from "@proto-kit/common";
|
|
2
|
+
import { container, injectable } from "tsyringe";
|
|
3
|
+
import {
|
|
4
|
+
NetworkState,
|
|
5
|
+
RuntimeTransaction,
|
|
6
|
+
RuntimeMethodExecutionContext,
|
|
7
|
+
RuntimeMethodExecutionData,
|
|
8
|
+
RuntimeMethodExecutionDataStruct,
|
|
9
|
+
} from "@proto-kit/protocol";
|
|
10
|
+
import { FlexibleProvablePure, Provable, Bool } from "o1js";
|
|
11
|
+
|
|
12
|
+
import { runtimeMethodNamesMetadataKey } from "../method/runtimeMethod";
|
|
13
|
+
|
|
14
|
+
import { RuntimeEnvironment } from "./RuntimeEnvironment";
|
|
15
|
+
|
|
16
|
+
const errors = {
|
|
17
|
+
inputDataNotSet: () => new Error("Input data for runtime execution not set"),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type EventRecord = Record<string, FlexibleProvablePure<any>>;
|
|
21
|
+
|
|
22
|
+
type InferProvable<T extends FlexibleProvablePure<any>> =
|
|
23
|
+
T extends Provable<infer U> ? U : never;
|
|
24
|
+
|
|
25
|
+
export class RuntimeEvents<Events extends EventRecord> {
|
|
26
|
+
public constructor(private readonly events: Events) {}
|
|
27
|
+
|
|
28
|
+
public emitIf<Key extends keyof Events>(
|
|
29
|
+
condition: Bool,
|
|
30
|
+
eventName: Key,
|
|
31
|
+
event: InferProvable<Events[Key]>
|
|
32
|
+
) {
|
|
33
|
+
if (this.events === undefined) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
"'events' property not defined, make sure to define the event types on your runtimemodule"
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
const eventType: FlexibleProvablePure<any> = this.events[eventName];
|
|
39
|
+
if (typeof eventName !== "string") {
|
|
40
|
+
throw new Error("Only string");
|
|
41
|
+
}
|
|
42
|
+
return container
|
|
43
|
+
.resolve(RuntimeMethodExecutionContext)
|
|
44
|
+
.addEvent(eventType, event, eventName, condition);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public emit<Key extends keyof Events>(
|
|
48
|
+
eventName: Key,
|
|
49
|
+
event: InferProvable<Events[Key]>
|
|
50
|
+
) {
|
|
51
|
+
this.emitIf(Bool(true), eventName, event);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Base class for runtime modules providing the necessary utilities.
|
|
57
|
+
*/
|
|
58
|
+
@injectable()
|
|
59
|
+
export class RuntimeModule<
|
|
60
|
+
Config = NoConfig,
|
|
61
|
+
> extends ConfigurableModule<Config> {
|
|
62
|
+
public static presets: Presets<unknown> = {};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Holds all method names that are callable throw transactions
|
|
66
|
+
*/
|
|
67
|
+
public readonly runtimeMethodNames: string[] = [];
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* This property exists only to typecheck that the RuntimeModule
|
|
71
|
+
* was extended correctly in e.g. a decorator. We need at least
|
|
72
|
+
* one non-optional property in this class to make the typechecking work.
|
|
73
|
+
*/
|
|
74
|
+
public isRuntimeModule = true;
|
|
75
|
+
|
|
76
|
+
public name?: string;
|
|
77
|
+
|
|
78
|
+
public runtime?: RuntimeEnvironment;
|
|
79
|
+
|
|
80
|
+
public events?: RuntimeEvents<any> = undefined;
|
|
81
|
+
|
|
82
|
+
public constructor() {
|
|
83
|
+
super();
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
85
|
+
const methodNames: string[] | undefined = Reflect.getMetadata(
|
|
86
|
+
runtimeMethodNamesMetadataKey,
|
|
87
|
+
this
|
|
88
|
+
);
|
|
89
|
+
this.runtimeMethodNames = methodNames ?? [];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public getInputs(): RuntimeMethodExecutionData {
|
|
93
|
+
return Provable.witness(RuntimeMethodExecutionDataStruct, () => {
|
|
94
|
+
const { input } = container.resolve<RuntimeMethodExecutionContext>(
|
|
95
|
+
RuntimeMethodExecutionContext
|
|
96
|
+
);
|
|
97
|
+
if (input === undefined) {
|
|
98
|
+
throw errors.inputDataNotSet();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return input;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public get transaction(): RuntimeTransaction {
|
|
106
|
+
return this.getInputs().transaction;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public get network(): NetworkState {
|
|
110
|
+
return this.getInputs().networkState;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Field } from "o1js";
|
|
2
|
+
import { SimpleAsyncStateService } from "@proto-kit/protocol";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Naive implementation of an in-memory variant of the StateService interface
|
|
6
|
+
*/
|
|
7
|
+
export class InMemoryStateService implements SimpleAsyncStateService {
|
|
8
|
+
/**
|
|
9
|
+
* This mapping container null values if the specific entry has been deleted.
|
|
10
|
+
* This is used by the CachedState service to keep track of deletions
|
|
11
|
+
*/
|
|
12
|
+
public values: Record<string, Field[] | null> = {};
|
|
13
|
+
|
|
14
|
+
public async get(key: Field): Promise<Field[] | undefined> {
|
|
15
|
+
return this.values[key.toString()] ?? undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public async set(key: Field, value: Field[] | undefined) {
|
|
19
|
+
if (value === undefined) {
|
|
20
|
+
this.values[key.toString()] = null;
|
|
21
|
+
} else {
|
|
22
|
+
this.values[key.toString()] = value;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Path, State } from "@proto-kit/protocol";
|
|
2
|
+
|
|
3
|
+
import type { RuntimeModule } from "../runtime/RuntimeModule.js";
|
|
4
|
+
|
|
5
|
+
const errors = {
|
|
6
|
+
missingName: (className: string) =>
|
|
7
|
+
new Error(
|
|
8
|
+
`Unable to provide a unique identifier for state, ${className} is missing a name.
|
|
9
|
+
Did you forget to extend your runtime module with 'extends RuntimeModule'?`
|
|
10
|
+
),
|
|
11
|
+
|
|
12
|
+
missingRuntime: (className: string) =>
|
|
13
|
+
new Error(
|
|
14
|
+
`Unable to provide 'runtime' for state, ${className} is missing a name.
|
|
15
|
+
Did you forget to extend your runtime module with 'extends RuntimeModule'?`
|
|
16
|
+
),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Decorates a runtime module property as state, passing down some
|
|
21
|
+
* underlying values to improve developer experience.
|
|
22
|
+
*/
|
|
23
|
+
export function state() {
|
|
24
|
+
return <TargetRuntimeModule extends RuntimeModule<unknown>>(
|
|
25
|
+
target: TargetRuntimeModule,
|
|
26
|
+
propertyKey: string
|
|
27
|
+
) => {
|
|
28
|
+
let value: State<unknown> | undefined;
|
|
29
|
+
|
|
30
|
+
Object.defineProperty(target, propertyKey, {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
|
|
33
|
+
get: function get() {
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
35
|
+
const self = this as TargetRuntimeModule;
|
|
36
|
+
|
|
37
|
+
if (self.name === undefined) {
|
|
38
|
+
throw errors.missingName(self.constructor.name);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!self.runtime) {
|
|
42
|
+
throw errors.missingRuntime(self.constructor.name);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const path = Path.fromProperty(self.name, propertyKey);
|
|
46
|
+
if (value) {
|
|
47
|
+
value.path = path;
|
|
48
|
+
|
|
49
|
+
// TODO: why is this complaining about `any`?
|
|
50
|
+
|
|
51
|
+
value.stateServiceProvider = self.runtime.stateServiceProvider;
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
set: (newValue: State<unknown>) => {
|
|
57
|
+
value = newValue;
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
describe("noop", () => {
|
|
2
|
+
it("noop", () => {
|
|
3
|
+
expect(1).toBe(1);
|
|
4
|
+
});
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
// TODO disabled because of the reflect-metadata error in the CI
|
|
8
|
+
// import "reflect-metadata";
|
|
9
|
+
// import { container } from "tsyringe";
|
|
10
|
+
// import {
|
|
11
|
+
// RuntimeMethodExecutionContext,
|
|
12
|
+
// RuntimeTransaction,
|
|
13
|
+
// NetworkState,
|
|
14
|
+
// } from "@proto-kit/protocol";
|
|
15
|
+
//
|
|
16
|
+
// import { MethodIdResolver } from "../src";
|
|
17
|
+
//
|
|
18
|
+
// import { Balances } from "./modules/Balances";
|
|
19
|
+
// import { createTestingRuntime } from "./TestingRuntime";
|
|
20
|
+
//
|
|
21
|
+
// describe.skip("runtime", () => {
|
|
22
|
+
// it("should encode methodnames correctly", () => {
|
|
23
|
+
// expect.assertions(2);
|
|
24
|
+
//
|
|
25
|
+
// const { runtime } = createTestingRuntime(
|
|
26
|
+
// {
|
|
27
|
+
// Balances,
|
|
28
|
+
// },
|
|
29
|
+
// {
|
|
30
|
+
// Balances: {},
|
|
31
|
+
// }
|
|
32
|
+
// );
|
|
33
|
+
//
|
|
34
|
+
// const balances = runtime.resolve("Balances");
|
|
35
|
+
//
|
|
36
|
+
// expect(balances).toBeDefined();
|
|
37
|
+
//
|
|
38
|
+
// console.log(Object.keys(balances));
|
|
39
|
+
// console.log(balances.getTotalSupply);
|
|
40
|
+
//
|
|
41
|
+
// const moduleName = "Balances";
|
|
42
|
+
// const methodName = "getTotalSupply";
|
|
43
|
+
//
|
|
44
|
+
// const methodId = runtime.dependencyContainer
|
|
45
|
+
// .resolve<MethodIdResolver>("MethodIdResolver")
|
|
46
|
+
// .getMethodId(moduleName, methodName);
|
|
47
|
+
// const method = runtime.getMethodById(methodId);
|
|
48
|
+
//
|
|
49
|
+
// expect(method).toBeDefined();
|
|
50
|
+
// });
|
|
51
|
+
//
|
|
52
|
+
// it("regression - check that analyzeMethods works on runtime", async () => {
|
|
53
|
+
// const { runtime } = createTestingRuntime(
|
|
54
|
+
// {
|
|
55
|
+
// Balances,
|
|
56
|
+
// },
|
|
57
|
+
// {
|
|
58
|
+
// Balances: {},
|
|
59
|
+
// }
|
|
60
|
+
// );
|
|
61
|
+
//
|
|
62
|
+
// const context = container.resolve(RuntimeMethodExecutionContext);
|
|
63
|
+
// context.setup({
|
|
64
|
+
// transaction: RuntimeTransaction.dummyTransaction(),
|
|
65
|
+
// networkState: NetworkState.empty(),
|
|
66
|
+
// });
|
|
67
|
+
//
|
|
68
|
+
// await runtime.zkProgrammable.zkProgram.analyzeMethods();
|
|
69
|
+
// });
|
|
70
|
+
// });
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ModulesConfig } from "@proto-kit/common";
|
|
2
|
+
import { StateServiceProvider } from "@proto-kit/protocol";
|
|
3
|
+
import { container } from "tsyringe";
|
|
4
|
+
|
|
5
|
+
import { InMemoryStateService, Runtime, RuntimeModulesRecord } from "../src";
|
|
6
|
+
|
|
7
|
+
export function createTestingRuntime<Modules extends RuntimeModulesRecord>(
|
|
8
|
+
modules: Modules,
|
|
9
|
+
config: ModulesConfig<Modules>
|
|
10
|
+
): {
|
|
11
|
+
runtime: Runtime<Modules>;
|
|
12
|
+
state: InMemoryStateService;
|
|
13
|
+
} {
|
|
14
|
+
const state = new InMemoryStateService();
|
|
15
|
+
|
|
16
|
+
const Runtimeclass = Runtime.from({
|
|
17
|
+
modules,
|
|
18
|
+
});
|
|
19
|
+
const runtime = new Runtimeclass();
|
|
20
|
+
|
|
21
|
+
runtime.configure(config);
|
|
22
|
+
|
|
23
|
+
runtime.create(() => container.createChildContainer());
|
|
24
|
+
|
|
25
|
+
runtime.dependencyContainer.register("AreProofsEnabled", {
|
|
26
|
+
useValue: {
|
|
27
|
+
areProofsEnabled: false,
|
|
28
|
+
|
|
29
|
+
setProofsEnabled(areProofsEnabled: boolean) {
|
|
30
|
+
this.areProofsEnabled = areProofsEnabled;
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
runtime.registerValue({
|
|
35
|
+
StateServiceProvider: new StateServiceProvider(),
|
|
36
|
+
Runtime: runtime,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
runtime.stateServiceProvider.setCurrentStateService(state);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
runtime,
|
|
43
|
+
state,
|
|
44
|
+
};
|
|
45
|
+
}
|