Skip to content

Commit 2d6b3d5

Browse files
authored
Merge pull request #241 from proto-kit/fix/improve-runtimemethod-decorator
Improve the @runtimeMethod decorator to enforce arguments to be provable types
2 parents a5843d2 + 92c6145 commit 2d6b3d5

File tree

4 files changed

+93
-65
lines changed

4 files changed

+93
-65
lines changed

packages/module/src/method/MethodParameterEncoder.ts

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ function getAllPropertyNamesOfPrototypeChain(type: unknown): string[] {
6262
);
6363
}
6464

65-
function isFlexibleProvablePure(
65+
export function isFlexibleProvablePure(
6666
type: unknown
6767
): type is FlexibleProvablePure<unknown> {
6868
// The required properties are defined on the prototype for Structs and CircuitValues
@@ -73,35 +73,43 @@ function isFlexibleProvablePure(
7373
return mandatory.every((prop) => props.includes(prop));
7474
}
7575

76-
export class MethodParameterEncoder {
77-
public static fromMethod(target: RuntimeModule<unknown>, methodName: string) {
78-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
79-
const paramtypes: ArgTypeArray = Reflect.getMetadata(
80-
"design:paramtypes",
81-
target,
82-
methodName
76+
export function checkArgsProvable(
77+
target: RuntimeModule<unknown>,
78+
methodName: string
79+
) {
80+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
81+
const paramtypes: ArgTypeArray = Reflect.getMetadata(
82+
"design:paramtypes",
83+
target,
84+
methodName
85+
);
86+
87+
if (paramtypes === undefined) {
88+
throw new Error(
89+
`Method with name ${methodName} doesn't exist on this module`
8390
);
91+
}
8492

85-
if (paramtypes === undefined) {
86-
throw new Error(
87-
`Method with name ${methodName} doesn't exist on this module`
88-
);
89-
}
93+
const indizes = paramtypes
94+
.map((type, index) => {
95+
if (isProofBaseType(type) || isFlexibleProvablePure(type)) {
96+
return undefined;
97+
}
98+
return `${index}`;
99+
})
100+
.filter(filterNonUndefined);
101+
if (indizes.length > 0) {
102+
const indexString = indizes.reduce((a, b) => `${a}, ${b}`);
103+
throw new Error(
104+
`Not all arguments of method '${target.name}.${methodName}' are provable types or proofs (indizes: [${indexString}])`
105+
);
106+
}
107+
return paramtypes;
108+
}
90109

91-
const indizes = paramtypes
92-
.map((type, index) => {
93-
if (isProofBaseType(type) || isFlexibleProvablePure(type)) {
94-
return undefined;
95-
}
96-
return `${index}`;
97-
})
98-
.filter(filterNonUndefined);
99-
if (indizes.length > 0) {
100-
const indexString = indizes.reduce((a, b) => `${a}, ${b}`);
101-
throw new Error(
102-
`Not all arguments of method '${target.name}.${methodName}' are provable types or proofs (indizes: [${indexString}])`
103-
);
104-
}
110+
export class MethodParameterEncoder {
111+
public static fromMethod(target: RuntimeModule<unknown>, methodName: string) {
112+
const paramtypes = checkArgsProvable(target, methodName);
105113

106114
return new MethodParameterEncoder(paramtypes);
107115
}

packages/module/src/method/runtimeMethod.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ import {
1717

1818
import type { RuntimeModule } from "../runtime/RuntimeModule.js";
1919

20-
import { MethodParameterEncoder } from "./MethodParameterEncoder";
20+
import {
21+
MethodParameterEncoder,
22+
checkArgsProvable,
23+
} from "./MethodParameterEncoder";
2124

2225
const errors = {
2326
runtimeNotProvided: (name: string) =>
@@ -196,11 +199,9 @@ function runtimeMethodInternal(options: {
196199
return (
197200
target: RuntimeModule<unknown>,
198201
methodName: string,
199-
descriptor: TypedPropertyDescriptor<
200-
// TODO Limit possible parameter types
201-
(...args: any[]) => Promise<any>
202-
>
202+
descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<any>>
203203
) => {
204+
checkArgsProvable(target, methodName);
204205
const executionContext = container.resolve<RuntimeMethodExecutionContext>(
205206
RuntimeMethodExecutionContext
206207
);

packages/module/test/method/MethodParameterEncoder.test.ts

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,9 @@ import {
77
ZkProgram,
88
Proof,
99
} from "o1js";
10-
import { NonMethods, noop } from "@proto-kit/common";
10+
import { NonMethods } from "@proto-kit/common";
1111

12-
import {
13-
MethodParameterEncoder,
14-
RuntimeModule,
15-
runtimeModule,
16-
runtimeMethod,
17-
} from "../../src";
12+
import { MethodParameterEncoder } from "../../src";
1813

1914
class TestStruct extends Struct({
2015
a: Field,
@@ -124,29 +119,3 @@ describe("MethodParameterEncoder", () => {
124119
);
125120
}, 30000);
126121
});
127-
128-
class TieredStruct extends TestStruct {}
129-
130-
@runtimeModule()
131-
class TestModule extends RuntimeModule {
132-
@runtimeMethod()
133-
public async foo(
134-
a: TieredStruct,
135-
b: PublicKey,
136-
c: Field,
137-
d: TestProof,
138-
e: string
139-
) {
140-
noop();
141-
}
142-
}
143-
144-
describe("MethodParameterEncoder construction", () => {
145-
it("should throw on non-provable method signature", () => {
146-
const module = new TestModule();
147-
module.name = "testModule";
148-
expect(() => MethodParameterEncoder.fromMethod(module, "foo")).toThrowError(
149-
"'testModule.foo' are provable types or proofs (indizes: [4])"
150-
);
151-
});
152-
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Bool, Field, PublicKey, Struct, ZkProgram } from "o1js";
2+
import { noop } from "@proto-kit/common";
3+
4+
import { runtimeMethod, RuntimeModule, runtimeModule } from "../../src";
5+
6+
class TestStruct extends Struct({
7+
a: Field,
8+
b: Bool,
9+
}) {}
10+
class TieredStruct extends TestStruct {}
11+
const TestProgram = ZkProgram({
12+
name: "TestProgram",
13+
publicInput: PublicKey,
14+
publicOutput: TestStruct,
15+
methods: {
16+
foo: {
17+
privateInputs: [],
18+
method: async (input: PublicKey) => {
19+
return {
20+
a: Field(input.x),
21+
b: Bool(input.isOdd),
22+
};
23+
},
24+
},
25+
},
26+
});
27+
class TestProof extends ZkProgram.Proof(TestProgram) {}
28+
29+
describe("Creating module with non-provable method argument", () => {
30+
it("should throw on non-provable method signature", () => {
31+
expect(() => {
32+
@runtimeModule()
33+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
34+
class TestModule extends RuntimeModule {
35+
@runtimeMethod()
36+
public async foo(
37+
a: TieredStruct,
38+
b: PublicKey,
39+
c: Field,
40+
d: TestProof,
41+
e: string
42+
) {
43+
noop();
44+
}
45+
}
46+
}).toThrow(
47+
"Not all arguments of method 'undefined.foo' are provable types or proofs (indizes: [4])"
48+
);
49+
});
50+
});

0 commit comments

Comments
 (0)