Skip to content

Commit 31c2013

Browse files
committed
Merge branch 'develop' into fix/tests-node-upgrade
2 parents 0e8d3c3 + f57b1c0 commit 31c2013

File tree

7 files changed

+207
-74
lines changed

7 files changed

+207
-74
lines changed

packages/common/src/config/ModuleContainer.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
DependencyContainer,
55
Frequency,
66
InjectionToken,
7-
instancePerContainerCachingFactory,
7+
instanceCachingFactory,
88
isClassProvider,
99
isFactoryProvider,
1010
isTokenProvider,
@@ -417,9 +417,7 @@ export class ModuleContainer<
417417
// this enables us to have a singletoned factory
418418
// that returns the same instance for each resolve
419419
this.container.register(key, {
420-
useFactory: instancePerContainerCachingFactory(
421-
declaration.useFactory
422-
),
420+
useFactory: instanceCachingFactory(declaration.useFactory),
423421
});
424422
} else if (isClassProvider(declaration)) {
425423
this.container.register(key, declaration, {

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+
});

packages/protocol/src/protocol/Protocol.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,16 @@ import { ProtocolEnvironment } from "./ProtocolEnvironment";
2828
import { ProvableBlockHook } from "./ProvableBlockHook";
2929
import { TransitioningProtocolModule } from "./TransitioningProtocolModule";
3030

31+
/**
32+
* This is a mapping of abstract classes to their respective injection tokens.
33+
* Keys are the abstract classes names, which need to be set dynamically
34+
* and can't be hardcoded since producing optimized builds may mangle the
35+
* class names, making them different from the ones in the source code.
36+
*/
3137
const PROTOCOL_INJECTION_TOKENS: Record<string, string> = {
32-
ProvableTransactionHook: "ProvableTransactionHook",
33-
ProvableBlockHook: "ProvableBlockHook",
34-
ProvableSettlementHook: "ProvableSettlementHook",
38+
[ProvableTransactionHook.name]: "ProvableTransactionHook",
39+
[ProvableBlockHook.name]: "ProvableBlockHook",
40+
[ProvableSettlementHook.name]: "ProvableSettlementHook",
3541
};
3642

3743
export type ProtocolModulesRecord = ModulesRecord<
@@ -162,7 +168,7 @@ export class Protocol<
162168

163169
implementingModules.forEach(([key]) => {
164170
this.container.register(
165-
abstractType.name,
171+
newInjectionToken,
166172
{ useToken: key },
167173
{ lifecycle: Lifecycle.ContainerScoped }
168174
);
@@ -175,7 +181,7 @@ export class Protocol<
175181

176182
// Register default (noop) version
177183
this.container.register(
178-
abstractType.name,
184+
newInjectionToken,
179185
{ useClass: defaultType },
180186
{ lifecycle: Lifecycle.ContainerScoped }
181187
);

packages/sequencer/test/integration/BlockProduction.test.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,107 @@ describe("block production", () => {
312312
expect(newState).toBeUndefined();
313313
}, 30_000);
314314

315+
it("should produce txs in non-consecutive blocks", async () => {
316+
log.setLevel("TRACE");
317+
318+
const privateKey = PrivateKey.random();
319+
const publicKey = privateKey.toPublicKey();
320+
321+
const privateKey2 = PrivateKey.random();
322+
const publicKey2 = privateKey2.toPublicKey();
323+
324+
await mempool.add(
325+
createTransaction({
326+
runtime,
327+
method: ["Balance", "setBalanceIf"],
328+
privateKey,
329+
args: [publicKey, UInt64.from(100), Bool(true)],
330+
nonce: 0,
331+
})
332+
);
333+
334+
// let [block, batch] = await blockTrigger.produceBlockAndBatch();
335+
const block = await blockTrigger.produceBlock();
336+
337+
expect(block).toBeDefined();
338+
339+
expect(block!.transactions).toHaveLength(1);
340+
expect(block!.transactions[0].status.toBoolean()).toBe(true);
341+
expect(block!.transactions[0].statusMessage).toBeUndefined();
342+
343+
expect(block!.transactions[0].stateTransitions).toHaveLength(1);
344+
expect(block!.transactions[0].protocolTransitions).toHaveLength(2);
345+
346+
await blockTrigger.produceBlock();
347+
348+
await mempool.add(
349+
createTransaction({
350+
runtime,
351+
method: ["Balance", "setBalanceIf"],
352+
privateKey: privateKey2,
353+
args: [publicKey2, UInt64.from(100), Bool(true)],
354+
nonce: 0,
355+
})
356+
);
357+
await blockTrigger.produceBlock();
358+
359+
await mempool.add(
360+
createTransaction({
361+
runtime,
362+
method: ["Balance", "setBalanceIf"],
363+
privateKey: privateKey2,
364+
args: [publicKey2, UInt64.from(100), Bool(true)],
365+
nonce: 1,
366+
})
367+
);
368+
await blockTrigger.produceBlock();
369+
370+
await mempool.add(
371+
createTransaction({
372+
runtime,
373+
method: ["Balance", "setBalanceIf"],
374+
privateKey: privateKey2,
375+
args: [publicKey2, UInt64.from(100), Bool(true)],
376+
nonce: 2,
377+
})
378+
);
379+
await blockTrigger.produceBlock();
380+
381+
await mempool.add(
382+
createTransaction({
383+
runtime,
384+
method: ["Balance", "setBalanceIf"],
385+
privateKey: privateKey2,
386+
args: [publicKey2, UInt64.from(100), Bool(true)],
387+
nonce: 3,
388+
})
389+
);
390+
await blockTrigger.produceBlock();
391+
392+
// Second tx
393+
await mempool.add(
394+
createTransaction({
395+
runtime,
396+
method: ["Balance", "setBalanceIf"],
397+
privateKey,
398+
args: [publicKey, UInt64.from(100), Bool(true)],
399+
nonce: 1,
400+
})
401+
);
402+
403+
log.info("Starting second block");
404+
405+
const block2 = await blockTrigger.produceBlock();
406+
407+
expect(block2).toBeDefined();
408+
409+
expect(block2!.transactions).toHaveLength(1);
410+
console.log(block2!.transactions[0]);
411+
console.log(block2!.transactions[0].statusMessage);
412+
expect(block2!.transactions[0].status.toBoolean()).toBe(true);
413+
expect(block2!.transactions[0].statusMessage).toBeUndefined();
414+
}, 60_000);
415+
315416
const numberTxs = 3;
316417

317418
it("should produce block with multiple transaction", async () => {

0 commit comments

Comments
 (0)