@proto-kit/module 0.1.1-develop.153
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/chain/Chain.d.ts +109 -0
- package/dist/chain/Chain.d.ts.map +1 -0
- package/dist/chain/Chain.js +229 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/method/MethodExecutionContext.d.ts +73 -0
- package/dist/method/MethodExecutionContext.d.ts.map +1 -0
- package/dist/method/MethodExecutionContext.js +112 -0
- package/dist/method/MethodParameterDecoder.d.ts +20 -0
- package/dist/method/MethodParameterDecoder.d.ts.map +1 -0
- package/dist/method/MethodParameterDecoder.js +30 -0
- package/dist/method/RuntimeMethodExecutionContext.d.ts +57 -0
- package/dist/method/RuntimeMethodExecutionContext.d.ts.map +1 -0
- package/dist/method/RuntimeMethodExecutionContext.js +92 -0
- package/dist/method/assert.d.ts +12 -0
- package/dist/method/assert.d.ts.map +1 -0
- package/dist/method/assert.js +20 -0
- package/dist/method/decorator.d.ts +45 -0
- package/dist/method/decorator.d.ts.map +1 -0
- package/dist/method/decorator.js +140 -0
- package/dist/method/runtimeMethod.d.ts +18 -0
- package/dist/method/runtimeMethod.d.ts.map +1 -0
- package/dist/method/runtimeMethod.js +114 -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/Runtime.d.ts +71 -0
- package/dist/runtime/Runtime.d.ts.map +1 -0
- package/dist/runtime/Runtime.js +185 -0
- package/dist/runtime/RuntimeModule.d.ts +30 -0
- package/dist/runtime/RuntimeModule.d.ts.map +1 -0
- package/dist/runtime/RuntimeModule.js +44 -0
- package/dist/state/InMemoryStateService.d.ts +14 -0
- package/dist/state/InMemoryStateService.d.ts.map +1 -0
- package/dist/state/InMemoryStateService.js +21 -0
- package/dist/state/State.d.ts +65 -0
- package/dist/state/State.d.ts.map +1 -0
- package/dist/state/State.js +114 -0
- package/dist/state/StateMap.d.ts +37 -0
- package/dist/state/StateMap.d.ts.map +1 -0
- package/dist/state/StateMap.js +56 -0
- package/dist/state/StateServiceProvider.d.ts +10 -0
- package/dist/state/StateServiceProvider.d.ts.map +1 -0
- package/dist/state/StateServiceProvider.js +34 -0
- package/dist/state/decorator.d.ts +7 -0
- package/dist/state/decorator.d.ts.map +1 -0
- package/dist/state/decorator.js +42 -0
- package/jest.config.cjs +1 -0
- package/package.json +36 -0
- package/src/index.ts +12 -0
- package/src/method/MethodParameterDecoder.ts +55 -0
- package/src/method/RuntimeMethodExecutionContext.ts +111 -0
- package/src/method/assert.test.ts +49 -0
- package/src/method/assert.ts +23 -0
- package/src/method/decorator.test.ts +46 -0
- package/src/method/runtimeMethod.ts +192 -0
- package/src/module/decorator.ts +21 -0
- package/src/runtime/Runtime.ts +304 -0
- package/src/runtime/RuntimeModule.ts +68 -0
- package/src/state/InMemoryStateService.ts +29 -0
- package/src/state/State.ts +154 -0
- package/src/state/StateMap.ts +69 -0
- package/src/state/StateServiceProvider.ts +24 -0
- package/src/state/decorator.ts +65 -0
- package/test/Runtime.test.ts +37 -0
- package/test/modules/Admin.ts +19 -0
- package/test/modules/Balances.test.ts +340 -0
- package/test/modules/Balances.ts +51 -0
- package/test/runtimeMethod.test.ts +43 -0
- package/test/transaction.test.ts +82 -0
- package/tsconfig.json +8 -0
- package/tsconfig.test.json +9 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { Bool, Field, FlexibleProvable, Struct } from "snarkyjs";
|
|
2
|
+
import { container } from "tsyringe";
|
|
3
|
+
import {
|
|
4
|
+
StateTransition,
|
|
5
|
+
DefaultProvableHashList,
|
|
6
|
+
ProvableStateTransition,
|
|
7
|
+
MethodPublicOutput,
|
|
8
|
+
} from "@proto-kit/protocol";
|
|
9
|
+
import { DecoratedMethod, toProver, ZkProgrammable } from "@proto-kit/common";
|
|
10
|
+
|
|
11
|
+
import type { RuntimeModule } from "../runtime/RuntimeModule.js";
|
|
12
|
+
|
|
13
|
+
import { RuntimeMethodExecutionContext } from "./RuntimeMethodExecutionContext.js";
|
|
14
|
+
|
|
15
|
+
const errors = {
|
|
16
|
+
runtimeNotProvided: (name: string) =>
|
|
17
|
+
new Error(`Runtime was not provided for module: ${name}`),
|
|
18
|
+
|
|
19
|
+
methodInputsNotProvided: () =>
|
|
20
|
+
new Error(
|
|
21
|
+
"Method execution inputs not provided, provide them via context.inputs"
|
|
22
|
+
),
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function toStateTransitionsHash(
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
+
stateTransitions: StateTransition<any>[]
|
|
28
|
+
) {
|
|
29
|
+
const stateTransitionsHashList = new DefaultProvableHashList(
|
|
30
|
+
ProvableStateTransition
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return stateTransitions
|
|
34
|
+
.map((stateTransition) => stateTransition.toProvable())
|
|
35
|
+
.reduce(
|
|
36
|
+
(allStateTransitionsHashList, stateTransition) =>
|
|
37
|
+
allStateTransitionsHashList.push(stateTransition),
|
|
38
|
+
stateTransitionsHashList
|
|
39
|
+
)
|
|
40
|
+
.toField();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// eslint-disable-next-line etc/prefer-interface
|
|
44
|
+
export type WrappedMethod = (...args: unknown[]) => MethodPublicOutput;
|
|
45
|
+
|
|
46
|
+
export function toWrappedMethod(
|
|
47
|
+
this: RuntimeModule<unknown>,
|
|
48
|
+
methodName: string,
|
|
49
|
+
moduleMethod: (...args: unknown[]) => unknown
|
|
50
|
+
) {
|
|
51
|
+
const executionContext = container.resolve<RuntimeMethodExecutionContext>(
|
|
52
|
+
RuntimeMethodExecutionContext
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const wrappedMethod: WrappedMethod = (...args): MethodPublicOutput => {
|
|
56
|
+
Reflect.apply(moduleMethod, this, args);
|
|
57
|
+
const {
|
|
58
|
+
result: { stateTransitions, status },
|
|
59
|
+
input,
|
|
60
|
+
} = executionContext.current();
|
|
61
|
+
|
|
62
|
+
const stateTransitionsHash = toStateTransitionsHash(stateTransitions);
|
|
63
|
+
|
|
64
|
+
if (input === undefined) {
|
|
65
|
+
throw errors.methodInputsNotProvided();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const transactionHash = input.transaction.hash();
|
|
69
|
+
const networkStateHash = input.networkState.hash();
|
|
70
|
+
|
|
71
|
+
return new MethodPublicOutput({
|
|
72
|
+
stateTransitionsHash,
|
|
73
|
+
status,
|
|
74
|
+
transactionHash,
|
|
75
|
+
networkStateHash,
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
Object.defineProperty(wrappedMethod, "name", {
|
|
80
|
+
value: `wrapped_${methodName}`,
|
|
81
|
+
writable: false,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return wrappedMethod;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function combineMethodName(
|
|
88
|
+
runtimeModuleName: string,
|
|
89
|
+
methodName: string
|
|
90
|
+
) {
|
|
91
|
+
return `${runtimeModuleName}.${methodName}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const runtimeMethodMetadataKey = "yab-method";
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Checks the metadata of the provided runtime module and its method,
|
|
98
|
+
* to see if it has been decorated with @runtimeMethod()
|
|
99
|
+
*
|
|
100
|
+
* @param target - Runtime module to check
|
|
101
|
+
* @param propertyKey - Name of the method to check in the prior runtime module
|
|
102
|
+
* @returns - If the provided method name is a runtime method or not
|
|
103
|
+
*/
|
|
104
|
+
export function isRuntimeMethod(
|
|
105
|
+
target: RuntimeModule<unknown>,
|
|
106
|
+
propertyKey: string
|
|
107
|
+
) {
|
|
108
|
+
return Boolean(
|
|
109
|
+
Reflect.getMetadata(runtimeMethodMetadataKey, target, propertyKey)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function runtimeMethod() {
|
|
114
|
+
return (
|
|
115
|
+
target: RuntimeModule<unknown>,
|
|
116
|
+
methodName: string,
|
|
117
|
+
descriptor: PropertyDescriptor
|
|
118
|
+
) => {
|
|
119
|
+
const executionContext = container.resolve<RuntimeMethodExecutionContext>(
|
|
120
|
+
RuntimeMethodExecutionContext
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
Reflect.defineMetadata(runtimeMethodMetadataKey, true, target, methodName);
|
|
124
|
+
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
126
|
+
const simulatedMethod = descriptor.value as DecoratedMethod;
|
|
127
|
+
|
|
128
|
+
descriptor.value = function value(
|
|
129
|
+
this: RuntimeModule<unknown>,
|
|
130
|
+
...args: FlexibleProvable<unknown>[]
|
|
131
|
+
) {
|
|
132
|
+
const constructorName = this.constructor.name;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* If its a top level method call, wrap it into a wrapped method,
|
|
136
|
+
* since it'll be turned into a real/mock prover in provableMethod().
|
|
137
|
+
*
|
|
138
|
+
* Otherwise provableMethod() will just call the originalMethod provided
|
|
139
|
+
* if method is not called at the top level.
|
|
140
|
+
*/
|
|
141
|
+
const simulatedWrappedMethod = Reflect.apply(toWrappedMethod, this, [
|
|
142
|
+
methodName,
|
|
143
|
+
simulatedMethod,
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Before the prover runs, make sure it is operating on the correct
|
|
148
|
+
* RuntimeMethodExecutionContext state, meaning it enters and exits
|
|
149
|
+
* the context properly.
|
|
150
|
+
*/
|
|
151
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
152
|
+
async function prover(this: ZkProgrammable<any, any>) {
|
|
153
|
+
executionContext.beforeMethod(constructorName, methodName, args);
|
|
154
|
+
const innerProver = toProver(
|
|
155
|
+
combineMethodName(constructorName, methodName),
|
|
156
|
+
simulatedWrappedMethod,
|
|
157
|
+
false,
|
|
158
|
+
...args
|
|
159
|
+
).bind(this);
|
|
160
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
161
|
+
let result: Awaited<ReturnType<typeof innerProver>>;
|
|
162
|
+
try {
|
|
163
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
164
|
+
result = await Reflect.apply(innerProver, this, args);
|
|
165
|
+
} finally {
|
|
166
|
+
executionContext.afterMethod();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
executionContext.beforeMethod(constructorName, methodName, args);
|
|
173
|
+
|
|
174
|
+
if (executionContext.isTopLevel) {
|
|
175
|
+
if (!this.runtime) {
|
|
176
|
+
throw errors.runtimeNotProvided(constructorName);
|
|
177
|
+
}
|
|
178
|
+
executionContext.setProver(prover.bind(this.runtime.zkProgrammable));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
182
|
+
let result: unknown;
|
|
183
|
+
try {
|
|
184
|
+
result = Reflect.apply(simulatedMethod, this, args);
|
|
185
|
+
} finally {
|
|
186
|
+
executionContext.afterMethod();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return result;
|
|
190
|
+
};
|
|
191
|
+
};
|
|
192
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { injectable } from "tsyringe";
|
|
2
|
+
import { StaticConfigurableModule, TypedClass } from "@proto-kit/common";
|
|
3
|
+
|
|
4
|
+
import { RuntimeModule } from "../runtime/RuntimeModule.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Marks the decorated class as a runtime module, while also
|
|
8
|
+
* making it injectable with our dependency injection solution.
|
|
9
|
+
*/
|
|
10
|
+
export function runtimeModule() {
|
|
11
|
+
return (
|
|
12
|
+
/**
|
|
13
|
+
* Check if the target class extends RuntimeModule, while
|
|
14
|
+
* also providing static config presets
|
|
15
|
+
*/
|
|
16
|
+
target: StaticConfigurableModule<unknown> &
|
|
17
|
+
TypedClass<RuntimeModule<unknown>>
|
|
18
|
+
) => {
|
|
19
|
+
injectable()(target);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
// eslint-disable-next-line max-len
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment */
|
|
3
|
+
import { Experimental } from "snarkyjs";
|
|
4
|
+
import { DependencyContainer, injectable } from "tsyringe";
|
|
5
|
+
import {
|
|
6
|
+
StringKeyOf,
|
|
7
|
+
ModuleContainer,
|
|
8
|
+
ModulesConfig,
|
|
9
|
+
ModulesRecord,
|
|
10
|
+
TypedClass,
|
|
11
|
+
ZkProgrammable,
|
|
12
|
+
PlainZkProgram,
|
|
13
|
+
WithZkProgrammable,
|
|
14
|
+
AreProofsEnabled,
|
|
15
|
+
} from "@proto-kit/common";
|
|
16
|
+
import {
|
|
17
|
+
fieldToString,
|
|
18
|
+
stringToField,
|
|
19
|
+
MethodPublicOutput,
|
|
20
|
+
} from "@proto-kit/protocol";
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
combineMethodName,
|
|
24
|
+
isRuntimeMethod,
|
|
25
|
+
toWrappedMethod,
|
|
26
|
+
WrappedMethod,
|
|
27
|
+
} from "../method/runtimeMethod.js";
|
|
28
|
+
import { StateService } from "../state/InMemoryStateService.js";
|
|
29
|
+
import { StateServiceProvider } from "../state/StateServiceProvider";
|
|
30
|
+
|
|
31
|
+
import { RuntimeModule } from "./RuntimeModule.js";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Record of modules accepted by the Runtime module container.
|
|
35
|
+
*
|
|
36
|
+
* We have to use TypedClass since RuntimeModule
|
|
37
|
+
* is an abstract class
|
|
38
|
+
*/
|
|
39
|
+
export type RuntimeModulesRecord = ModulesRecord<
|
|
40
|
+
TypedClass<RuntimeModule<unknown>>
|
|
41
|
+
>;
|
|
42
|
+
|
|
43
|
+
const errors = {
|
|
44
|
+
methodNotFound: (methodKey: string) =>
|
|
45
|
+
new Error(`Unable to find method with id ${methodKey}`),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Definition / required arguments for the Runtime class
|
|
50
|
+
*/
|
|
51
|
+
export interface RuntimeDefinition<Modules extends RuntimeModulesRecord> {
|
|
52
|
+
state: StateService;
|
|
53
|
+
modules: Modules;
|
|
54
|
+
config?: ModulesConfig<Modules>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export class RuntimeZkProgrammable<
|
|
58
|
+
Modules extends RuntimeModulesRecord
|
|
59
|
+
> extends ZkProgrammable<undefined, MethodPublicOutput> {
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
61
|
+
public constructor(public runtime: Runtime<Modules>) {
|
|
62
|
+
super();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public get appChain() {
|
|
66
|
+
return this.runtime.appChain;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public zkProgramFactory(): PlainZkProgram<undefined, MethodPublicOutput> {
|
|
70
|
+
type Methods = Record<
|
|
71
|
+
string,
|
|
72
|
+
{
|
|
73
|
+
privateInputs: any;
|
|
74
|
+
method: WrappedMethod;
|
|
75
|
+
}
|
|
76
|
+
>;
|
|
77
|
+
// We need to use explicit type annotations here,
|
|
78
|
+
// therefore we can't use destructuring
|
|
79
|
+
// eslint-disable-next-line max-len
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define,prefer-destructuring,putout/putout
|
|
81
|
+
const runtime: Runtime<Modules> = this.runtime;
|
|
82
|
+
|
|
83
|
+
const runtimeMethods = runtime.runtimeModuleNames.reduce<Methods>(
|
|
84
|
+
(allMethods, runtimeModuleName) => {
|
|
85
|
+
runtime.isValidModuleName(
|
|
86
|
+
runtime.definition.modules,
|
|
87
|
+
runtimeModuleName
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Couldnt find a better way to circumvent the type assertion
|
|
92
|
+
* regarding resolving only known modules. We assert in the line above
|
|
93
|
+
* but we cast it to any anyways to satisfy the proof system.
|
|
94
|
+
*/
|
|
95
|
+
// eslint-disable-next-line max-len
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
97
|
+
const runtimeModule = runtime.resolve(runtimeModuleName as any);
|
|
98
|
+
|
|
99
|
+
// eslint-disable-next-line max-len
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
101
|
+
const modulePrototype = Object.getPrototypeOf(runtimeModule) as Record<
|
|
102
|
+
string,
|
|
103
|
+
(...args: unknown[]) => unknown
|
|
104
|
+
>;
|
|
105
|
+
|
|
106
|
+
const modulePrototypeMethods =
|
|
107
|
+
Object.getOwnPropertyNames(modulePrototype);
|
|
108
|
+
|
|
109
|
+
const moduleMethods = modulePrototypeMethods.reduce<Methods>(
|
|
110
|
+
(allModuleMethods, methodName) => {
|
|
111
|
+
if (isRuntimeMethod(runtimeModule, methodName)) {
|
|
112
|
+
const combinedMethodName = combineMethodName(
|
|
113
|
+
runtimeModuleName,
|
|
114
|
+
methodName
|
|
115
|
+
);
|
|
116
|
+
const method = modulePrototype[methodName];
|
|
117
|
+
const wrappedMethod = Reflect.apply(
|
|
118
|
+
toWrappedMethod,
|
|
119
|
+
runtimeModule,
|
|
120
|
+
[methodName, method]
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// eslint-disable-next-line no-warning-comments
|
|
124
|
+
// TODO: find out how to import the Tuple type
|
|
125
|
+
|
|
126
|
+
const privateInputs = Reflect.getMetadata(
|
|
127
|
+
"design:paramtypes",
|
|
128
|
+
runtimeModule,
|
|
129
|
+
methodName
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
...allModuleMethods,
|
|
134
|
+
|
|
135
|
+
[combinedMethodName]: {
|
|
136
|
+
privateInputs,
|
|
137
|
+
method: wrappedMethod,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return allModuleMethods;
|
|
143
|
+
},
|
|
144
|
+
{}
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
...allMethods,
|
|
149
|
+
...moduleMethods,
|
|
150
|
+
};
|
|
151
|
+
},
|
|
152
|
+
{}
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const sortedRuntimeMethods = Object.fromEntries(
|
|
156
|
+
// eslint-disable-next-line @typescript-eslint/require-array-sort-compare
|
|
157
|
+
Object.entries(runtimeMethods).sort()
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const program = Experimental.ZkProgram({
|
|
161
|
+
publicOutput: MethodPublicOutput,
|
|
162
|
+
methods: sortedRuntimeMethods,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const SelfProof = Experimental.ZkProgram.Proof(program);
|
|
166
|
+
|
|
167
|
+
const methods = Object.keys(sortedRuntimeMethods).reduce<
|
|
168
|
+
Record<string, any>
|
|
169
|
+
>((boundMethods, methodName) => {
|
|
170
|
+
boundMethods[methodName] = program[methodName].bind(program);
|
|
171
|
+
return boundMethods;
|
|
172
|
+
}, {});
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
compile: program.compile.bind(program),
|
|
176
|
+
verify: program.verify.bind(program),
|
|
177
|
+
Proof: SelfProof,
|
|
178
|
+
methods,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Wrapper for an application specific runtime, which helps orchestrate
|
|
185
|
+
* runtime modules into an interoperable runtime.
|
|
186
|
+
*/
|
|
187
|
+
@injectable()
|
|
188
|
+
export class Runtime<Modules extends RuntimeModulesRecord>
|
|
189
|
+
extends ModuleContainer<Modules>
|
|
190
|
+
implements WithZkProgrammable<undefined, MethodPublicOutput>
|
|
191
|
+
{
|
|
192
|
+
public static from<Modules extends RuntimeModulesRecord>(
|
|
193
|
+
definition: RuntimeDefinition<Modules>
|
|
194
|
+
) {
|
|
195
|
+
return new Runtime(definition);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// runtime modules composed into a ZkProgram
|
|
199
|
+
public program?: ReturnType<typeof Experimental.ZkProgram>;
|
|
200
|
+
|
|
201
|
+
public definition: RuntimeDefinition<Modules>;
|
|
202
|
+
|
|
203
|
+
public zkProgrammable: ZkProgrammable<undefined, MethodPublicOutput>;
|
|
204
|
+
|
|
205
|
+
// eslint-disable-next-line no-warning-comments
|
|
206
|
+
// TODO DI
|
|
207
|
+
private readonly stateServiceProviderInstance = new StateServiceProvider(
|
|
208
|
+
this.definition.state
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Creates a new Runtime from the provided config
|
|
213
|
+
*
|
|
214
|
+
* @param modules - Configuration object for the constructed Runtime
|
|
215
|
+
*/
|
|
216
|
+
public constructor(definition: RuntimeDefinition<Modules>) {
|
|
217
|
+
super(definition);
|
|
218
|
+
this.definition = definition;
|
|
219
|
+
this.zkProgrammable = new RuntimeZkProgrammable<Modules>(this);
|
|
220
|
+
// this.registerValue({
|
|
221
|
+
// Runtime: this,
|
|
222
|
+
// });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
public get appChain(): AreProofsEnabled | undefined {
|
|
226
|
+
return this.container.resolve<AreProofsEnabled>("AppChain");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
public get stateService(): StateService {
|
|
230
|
+
return this.stateServiceProviderInstance.stateService;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
public get stateServiceProvider(): StateServiceProvider {
|
|
234
|
+
return this.stateServiceProviderInstance;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @returns The dependency injection container of this runtime
|
|
239
|
+
*/
|
|
240
|
+
public get dependencyContainer(): DependencyContainer {
|
|
241
|
+
return this.container;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* @param methodId The encoded name of the method to call.
|
|
246
|
+
* Encoding: "stringToField(module.name) << 128 + stringToField(method-name)"
|
|
247
|
+
*/
|
|
248
|
+
public getMethodById(methodId: bigint): (...args: unknown[]) => unknown {
|
|
249
|
+
const [moduleName, methodName] = this.getMethodNameFromId(methodId);
|
|
250
|
+
|
|
251
|
+
this.isValidModuleName(this.definition.modules, moduleName);
|
|
252
|
+
const module = this.resolve(moduleName);
|
|
253
|
+
|
|
254
|
+
// eslint-disable-next-line max-len
|
|
255
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions,@typescript-eslint/no-unsafe-member-access
|
|
256
|
+
const method = (module as any)[methodName];
|
|
257
|
+
if (method === undefined) {
|
|
258
|
+
throw errors.methodNotFound(`${moduleName}.${methodName}`);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
262
|
+
return (method as (...args: unknown[]) => unknown).bind(module);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
public getMethodNameFromId(methodId: bigint): [string, string] {
|
|
266
|
+
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
|
267
|
+
const moduleName = fieldToString(methodId >> 128n);
|
|
268
|
+
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
|
269
|
+
const methodName = fieldToString(methodId % 2n ** 128n);
|
|
270
|
+
|
|
271
|
+
return [moduleName, methodName];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
public getMethodId(moduleName: string, methodName: string): bigint {
|
|
275
|
+
return (
|
|
276
|
+
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
|
277
|
+
(stringToField(moduleName).toBigInt() << 128n) +
|
|
278
|
+
stringToField(methodName).toBigInt()
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Add a name and other respective properties required by RuntimeModules,
|
|
284
|
+
* that come from the current Runtime
|
|
285
|
+
*
|
|
286
|
+
* @param name - Name of the runtime module to decorate
|
|
287
|
+
*/
|
|
288
|
+
public decorateModule(
|
|
289
|
+
moduleName: StringKeyOf<Modules>,
|
|
290
|
+
containedModule: InstanceType<Modules[StringKeyOf<Modules>]>
|
|
291
|
+
) {
|
|
292
|
+
containedModule.name = moduleName;
|
|
293
|
+
containedModule.runtime = this;
|
|
294
|
+
|
|
295
|
+
super.decorateModule(moduleName, containedModule);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* @returns A list of names of all the registered module names
|
|
300
|
+
*/
|
|
301
|
+
public get runtimeModuleNames() {
|
|
302
|
+
return Object.keys(this.definition.modules);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ConfigurableModule, Presets } from "@proto-kit/common";
|
|
2
|
+
import { container, injectable } from "tsyringe";
|
|
3
|
+
import { NetworkState, RuntimeTransaction } from "@proto-kit/protocol";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
RuntimeMethodExecutionContext,
|
|
7
|
+
RuntimeMethodExecutionData,
|
|
8
|
+
} from "../method/RuntimeMethodExecutionContext";
|
|
9
|
+
import { StateService } from "../state/InMemoryStateService";
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
Runtime,
|
|
13
|
+
RuntimeDefinition,
|
|
14
|
+
RuntimeModulesRecord,
|
|
15
|
+
} from "./Runtime";
|
|
16
|
+
|
|
17
|
+
const errors = {
|
|
18
|
+
inputDataNotSet: () => new Error("Input data for runtime execution not set"),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* This type exists to carry over certain runtime properties
|
|
23
|
+
* to runtime modules, until we can inject them through DI.
|
|
24
|
+
*/
|
|
25
|
+
export interface PartialRuntime
|
|
26
|
+
extends Pick<Runtime<RuntimeModulesRecord>, "zkProgrammable"> {
|
|
27
|
+
definition: Pick<RuntimeDefinition<RuntimeModulesRecord>, "state">;
|
|
28
|
+
|
|
29
|
+
get stateService(): StateService;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Base class for runtime modules providing the necessary utilities.
|
|
34
|
+
*/
|
|
35
|
+
@injectable()
|
|
36
|
+
export class RuntimeModule<Config> extends ConfigurableModule<Config> {
|
|
37
|
+
public static presets: Presets<unknown> = {};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* This property exists only to typecheck that the RuntimeModule
|
|
41
|
+
* was extended correctly in e.g. a decorator. We need at least
|
|
42
|
+
* one non-optional property in this class to make the typechecking work.
|
|
43
|
+
*/
|
|
44
|
+
public isRuntimeModule = true;
|
|
45
|
+
|
|
46
|
+
public name?: string;
|
|
47
|
+
|
|
48
|
+
public runtime?: Runtime<RuntimeModulesRecord>;
|
|
49
|
+
|
|
50
|
+
private getInputs(): RuntimeMethodExecutionData {
|
|
51
|
+
const { input } = container.resolve<RuntimeMethodExecutionContext>(
|
|
52
|
+
RuntimeMethodExecutionContext
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (input === undefined) {
|
|
56
|
+
throw errors.inputDataNotSet();
|
|
57
|
+
}
|
|
58
|
+
return input;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public get transaction(): RuntimeTransaction {
|
|
62
|
+
return this.getInputs().transaction;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public get network(): NetworkState {
|
|
66
|
+
return this.getInputs().networkState;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Field } from "snarkyjs";
|
|
2
|
+
|
|
3
|
+
export interface StateService {
|
|
4
|
+
get: (key: Field) => Field[] | undefined;
|
|
5
|
+
set: (key: Field, value: Field[] | undefined) => void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Naive implementation of a StateService for testing purposes
|
|
10
|
+
*/
|
|
11
|
+
export class InMemoryStateService implements StateService {
|
|
12
|
+
public values: Record<string, Field[] | undefined> = {};
|
|
13
|
+
|
|
14
|
+
public get(key: Field): Field[] | undefined {
|
|
15
|
+
return this.values[key.toString()];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public set(key: Field, value: Field[] | undefined) {
|
|
19
|
+
if (
|
|
20
|
+
value === undefined &&
|
|
21
|
+
Object.prototype.hasOwnProperty.call(this.values, key.toString())
|
|
22
|
+
) {
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
24
|
+
delete this.values[key.toString()];
|
|
25
|
+
} else {
|
|
26
|
+
this.values[key.toString()] = value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|