@proto-kit/module 0.1.1-develop.0
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 +11 -0
- package/dist/factories/MethodIdFactory.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/method/MethodParameterEncoder.d.ts +26 -0
- package/dist/method/MethodParameterEncoder.d.ts.map +1 -0
- package/dist/method/MethodParameterEncoder.js +169 -0
- package/dist/method/MethodParameterEncoder.js.map +1 -0
- package/dist/method/runtimeMethod.d.ts +33 -0
- package/dist/method/runtimeMethod.d.ts.map +1 -0
- package/dist/method/runtimeMethod.js +169 -0
- package/dist/method/runtimeMethod.js.map +1 -0
- package/dist/module/decorator.d.ts +8 -0
- package/dist/module/decorator.d.ts.map +1 -0
- package/dist/module/decorator.js +16 -0
- package/dist/module/decorator.js.map +1 -0
- package/dist/runtime/MethodIdResolver.d.ts +20 -0
- package/dist/runtime/MethodIdResolver.d.ts.map +1 -0
- package/dist/runtime/MethodIdResolver.js +91 -0
- package/dist/runtime/MethodIdResolver.js.map +1 -0
- package/dist/runtime/Runtime.d.ts +72 -0
- package/dist/runtime/Runtime.d.ts.map +1 -0
- package/dist/runtime/Runtime.js +231 -0
- package/dist/runtime/Runtime.js.map +1 -0
- package/dist/runtime/RuntimeEnvironment.d.ts +10 -0
- package/dist/runtime/RuntimeEnvironment.d.ts.map +1 -0
- package/dist/runtime/RuntimeEnvironment.js +2 -0
- package/dist/runtime/RuntimeEnvironment.js.map +1 -0
- package/dist/runtime/RuntimeModule.d.ts +37 -0
- package/dist/runtime/RuntimeModule.d.ts.map +1 -0
- package/dist/runtime/RuntimeModule.js +80 -0
- package/dist/runtime/RuntimeModule.js.map +1 -0
- package/dist/state/InMemoryStateService.d.ts +15 -0
- package/dist/state/InMemoryStateService.d.ts.map +1 -0
- package/dist/state/InMemoryStateService.js +24 -0
- package/dist/state/InMemoryStateService.js.map +1 -0
- package/dist/state/decorator.d.ts +7 -0
- package/dist/state/decorator.d.ts.map +1 -0
- package/dist/state/decorator.js +40 -0
- package/dist/state/decorator.js.map +1 -0
- package/jest.config.cjs +12 -0
- package/package.json +35 -0
- package/src/factories/MethodIdFactory.ts +13 -0
- package/src/index.ts +10 -0
- package/src/method/MethodParameterEncoder.ts +260 -0
- package/src/method/runtimeMethod.ts +308 -0
- package/src/module/decorator.ts +21 -0
- package/src/runtime/MethodIdResolver.ts +108 -0
- package/src/runtime/Runtime.ts +395 -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 +121 -0
- package/test/method/runtimeMethod-fail.test.ts +50 -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,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
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Struct,
|
|
3
|
+
Field,
|
|
4
|
+
Bool,
|
|
5
|
+
PublicKey,
|
|
6
|
+
PrivateKey,
|
|
7
|
+
ZkProgram,
|
|
8
|
+
Proof,
|
|
9
|
+
} from "o1js";
|
|
10
|
+
import { NonMethods } from "@proto-kit/common";
|
|
11
|
+
|
|
12
|
+
import { MethodParameterEncoder } from "../../src";
|
|
13
|
+
|
|
14
|
+
class TestStruct extends Struct({
|
|
15
|
+
a: Field,
|
|
16
|
+
b: Bool,
|
|
17
|
+
}) {}
|
|
18
|
+
|
|
19
|
+
const TestProgram = ZkProgram({
|
|
20
|
+
name: "TestProgram",
|
|
21
|
+
publicInput: PublicKey,
|
|
22
|
+
publicOutput: TestStruct,
|
|
23
|
+
methods: {
|
|
24
|
+
foo: {
|
|
25
|
+
privateInputs: [],
|
|
26
|
+
method: async (input: PublicKey) => {
|
|
27
|
+
return {
|
|
28
|
+
a: Field(input.x),
|
|
29
|
+
b: Bool(input.isOdd),
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
class TestProof extends ZkProgram.Proof(TestProgram) {}
|
|
36
|
+
|
|
37
|
+
describe("MethodParameterEncoder", () => {
|
|
38
|
+
it("should en/decode Structs correctly", async () => {
|
|
39
|
+
expect.assertions(7);
|
|
40
|
+
|
|
41
|
+
const encoder = new MethodParameterEncoder([TestStruct]);
|
|
42
|
+
const { fields, auxiliary } = encoder.encode([
|
|
43
|
+
{ a: Field(2), b: Bool(true) },
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
expect(auxiliary).toHaveLength(0);
|
|
47
|
+
expect(fields).toHaveLength(2);
|
|
48
|
+
expect(fields[0].toString()).toBe("2");
|
|
49
|
+
expect(fields[1].toString()).toStrictEqual(Bool(true).toField().toString());
|
|
50
|
+
|
|
51
|
+
const decoded = await encoder.decode(fields, auxiliary);
|
|
52
|
+
expect(decoded).toHaveLength(1);
|
|
53
|
+
const decoded1 = decoded[0] as unknown as NonMethods<TestStruct>;
|
|
54
|
+
expect(decoded1.a.toString()).toStrictEqual("2");
|
|
55
|
+
expect(decoded1.b.toString()).toStrictEqual("true");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("should en/decode CircuitValues correctly", async () => {
|
|
59
|
+
expect.assertions(6);
|
|
60
|
+
|
|
61
|
+
const encoder = new MethodParameterEncoder([PublicKey]);
|
|
62
|
+
const pk = PrivateKey.random().toPublicKey();
|
|
63
|
+
|
|
64
|
+
const { fields, auxiliary } = encoder.encode([pk]);
|
|
65
|
+
|
|
66
|
+
expect(auxiliary).toHaveLength(0);
|
|
67
|
+
expect(fields).toHaveLength(2);
|
|
68
|
+
expect(fields.map((x) => x.toString())).toStrictEqual(
|
|
69
|
+
pk.toFields().map((x) => x.toString())
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const decoded = await encoder.decode(fields, auxiliary);
|
|
73
|
+
expect(decoded).toHaveLength(1);
|
|
74
|
+
|
|
75
|
+
const decoded1 = decoded[0] as unknown as PublicKey;
|
|
76
|
+
expect(decoded1.x.toString()).toStrictEqual(pk.x.toString());
|
|
77
|
+
expect(decoded1.isOdd.toString()).toStrictEqual(pk.isOdd.toString());
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("should en/decode Proofs correctly", async () => {
|
|
81
|
+
expect.assertions(13);
|
|
82
|
+
|
|
83
|
+
const encoder = new MethodParameterEncoder([TestProof]);
|
|
84
|
+
const input = PrivateKey.random().toPublicKey();
|
|
85
|
+
const output = { a: input.x, b: input.isOdd };
|
|
86
|
+
const dummy = await TestProof.dummy(input, output, 0);
|
|
87
|
+
|
|
88
|
+
const { fields, auxiliary } = encoder.encode([dummy]);
|
|
89
|
+
|
|
90
|
+
expect(auxiliary).toHaveLength(1);
|
|
91
|
+
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
93
|
+
const json = JSON.parse(auxiliary[0]);
|
|
94
|
+
|
|
95
|
+
expect(json).toHaveProperty("maxProofsVerified");
|
|
96
|
+
expect(json).toHaveProperty("proof");
|
|
97
|
+
expect(json.maxProofsVerified).toStrictEqual(0);
|
|
98
|
+
expect(json.proof.length).toBeGreaterThan(20);
|
|
99
|
+
|
|
100
|
+
expect(fields).toHaveLength(4);
|
|
101
|
+
expect(fields.map((x) => x.toString())).toStrictEqual(
|
|
102
|
+
[...input.toFields(), ...TestStruct.toFields(output)].map((x) =>
|
|
103
|
+
x.toString()
|
|
104
|
+
)
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const decoded = await encoder.decode(fields, auxiliary);
|
|
108
|
+
expect(decoded).toHaveLength(1);
|
|
109
|
+
|
|
110
|
+
const decoded1 = decoded[0] as unknown as Proof<PublicKey, TestStruct>;
|
|
111
|
+
expect(decoded1.maxProofsVerified).toStrictEqual(0);
|
|
112
|
+
expect(decoded1.proof).toBeDefined();
|
|
113
|
+
expect(decoded1.publicInput.equals(input).toBoolean()).toStrictEqual(true);
|
|
114
|
+
expect(decoded1.publicOutput.a.equals(output.a).toBoolean()).toStrictEqual(
|
|
115
|
+
true
|
|
116
|
+
);
|
|
117
|
+
expect(decoded1.publicOutput.b.equals(output.b).toBoolean()).toStrictEqual(
|
|
118
|
+
true
|
|
119
|
+
);
|
|
120
|
+
}, 30000);
|
|
121
|
+
});
|
|
@@ -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,0 +1,46 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
|
|
3
|
+
import { Bool, Field } from "o1js";
|
|
4
|
+
import { Option, StateTransition } from "@proto-kit/protocol";
|
|
5
|
+
|
|
6
|
+
import { toStateTransitionsHash } from "../../src/method/runtimeMethod";
|
|
7
|
+
|
|
8
|
+
describe.skip("toStateTransitionsHash", () => {
|
|
9
|
+
const noneStateTransition = StateTransition.from(
|
|
10
|
+
Field(0),
|
|
11
|
+
new Option(Bool(false), Field(0), Field)
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const someStateTransition = StateTransition.from(
|
|
15
|
+
Field(0),
|
|
16
|
+
new Option(Bool(true), Field(0), Field)
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
it.each([
|
|
20
|
+
[
|
|
21
|
+
[noneStateTransition],
|
|
22
|
+
"7067243248312463521220230733411703436580237248681301130001246160136823979683",
|
|
23
|
+
],
|
|
24
|
+
[
|
|
25
|
+
[someStateTransition],
|
|
26
|
+
"12841542804403638489097503092490970035615082088155587790175618374946575398395",
|
|
27
|
+
],
|
|
28
|
+
[
|
|
29
|
+
[noneStateTransition, someStateTransition],
|
|
30
|
+
"20641278138648130746922286021889771603940136555847557324578879341962747943601",
|
|
31
|
+
],
|
|
32
|
+
[
|
|
33
|
+
[someStateTransition, noneStateTransition],
|
|
34
|
+
"10362098987098600767020985423446775093761176563902435645494178193997179006954",
|
|
35
|
+
],
|
|
36
|
+
])(
|
|
37
|
+
"should calculate a hash of all provided state transitions",
|
|
38
|
+
(stateTransitions, expectedHash) => {
|
|
39
|
+
expect.assertions(1);
|
|
40
|
+
|
|
41
|
+
const hash = toStateTransitionsHash(stateTransitions).toString();
|
|
42
|
+
|
|
43
|
+
expect(hash).toBe(expectedHash);
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PublicKey } from "o1js";
|
|
2
|
+
import { assert } from "@proto-kit/protocol";
|
|
3
|
+
|
|
4
|
+
import { runtimeModule } from "../../src/module/decorator.js";
|
|
5
|
+
import { RuntimeModule } from "../../src/runtime/RuntimeModule.js";
|
|
6
|
+
import { runtimeMethod } from "../../src/method/runtimeMethod.js";
|
|
7
|
+
|
|
8
|
+
interface AdminConfig {
|
|
9
|
+
publicKey: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@runtimeModule()
|
|
13
|
+
export class Admin extends RuntimeModule<AdminConfig> {
|
|
14
|
+
@runtimeMethod()
|
|
15
|
+
public async isAdmin(publicKey: PublicKey) {
|
|
16
|
+
const admin = PublicKey.empty<typeof PublicKey>().toConstant();
|
|
17
|
+
assert(admin.equals(publicKey));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { Field, Poseidon, PrivateKey, Proof, PublicKey, UInt64 } from "o1js";
|
|
3
|
+
import { container } from "tsyringe";
|
|
4
|
+
import {
|
|
5
|
+
type ProvableStateTransition,
|
|
6
|
+
Path,
|
|
7
|
+
MethodPublicOutput,
|
|
8
|
+
SimpleAsyncStateService,
|
|
9
|
+
RuntimeMethodExecutionContext,
|
|
10
|
+
RuntimeTransaction,
|
|
11
|
+
NetworkState,
|
|
12
|
+
} from "@proto-kit/protocol";
|
|
13
|
+
|
|
14
|
+
import { Runtime } from "../../src";
|
|
15
|
+
import { createTestingRuntime } from "../TestingRuntime";
|
|
16
|
+
|
|
17
|
+
import { Balances } from "./Balances.js";
|
|
18
|
+
import { Admin } from "./Admin.js";
|
|
19
|
+
|
|
20
|
+
describe("balances", () => {
|
|
21
|
+
let balances: Balances;
|
|
22
|
+
|
|
23
|
+
let state: SimpleAsyncStateService;
|
|
24
|
+
|
|
25
|
+
let runtime: Runtime<{
|
|
26
|
+
Admin: typeof Admin;
|
|
27
|
+
Balances: typeof Balances;
|
|
28
|
+
}>;
|
|
29
|
+
|
|
30
|
+
async function getStateValue(path: Field | undefined) {
|
|
31
|
+
if (!path) {
|
|
32
|
+
throw new Error("Path not found");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const stateValue = await state.get(path);
|
|
36
|
+
|
|
37
|
+
if (!stateValue) {
|
|
38
|
+
throw new Error("stateValue is undefined");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return stateValue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function createChain() {
|
|
45
|
+
({ runtime, state } = createTestingRuntime(
|
|
46
|
+
{
|
|
47
|
+
Balances,
|
|
48
|
+
Admin,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
Admin: {
|
|
52
|
+
publicKey: PublicKey.empty<typeof PublicKey>().toBase58(),
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
Balances: {},
|
|
56
|
+
}
|
|
57
|
+
));
|
|
58
|
+
|
|
59
|
+
balances = runtime.resolve("Balances");
|
|
60
|
+
|
|
61
|
+
await state.set(balances.totalSupply.path!, UInt64.from(10).toFields());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
describe.skip("compile and prove", () => {
|
|
65
|
+
beforeAll(createChain);
|
|
66
|
+
|
|
67
|
+
// Disabled until we implement a mechanism to enable/disable compiling tests
|
|
68
|
+
it("should compile and prove a method execution", async () => {
|
|
69
|
+
expect.assertions(3);
|
|
70
|
+
|
|
71
|
+
runtime.zkProgrammable.areProofsEnabled?.setProofsEnabled(true);
|
|
72
|
+
|
|
73
|
+
const executionContext = container.resolve(RuntimeMethodExecutionContext);
|
|
74
|
+
executionContext.setup({
|
|
75
|
+
transaction: RuntimeTransaction.dummyTransaction(),
|
|
76
|
+
networkState: NetworkState.empty(),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const expectedStateTransitionsHash =
|
|
80
|
+
"1439144406936083177718146178121957896974210157062549589517697792374542035761";
|
|
81
|
+
const expectedStatus = true;
|
|
82
|
+
|
|
83
|
+
await runtime.zkProgrammable.zkProgram[0].compile();
|
|
84
|
+
|
|
85
|
+
await balances.getTotalSupply();
|
|
86
|
+
|
|
87
|
+
const { result } = executionContext.current();
|
|
88
|
+
|
|
89
|
+
const proof = await result.prove<Proof<undefined, MethodPublicOutput>>();
|
|
90
|
+
|
|
91
|
+
const verified = await runtime.zkProgrammable.zkProgram[0].verify(proof);
|
|
92
|
+
|
|
93
|
+
runtime.zkProgrammable.areProofsEnabled?.setProofsEnabled(false);
|
|
94
|
+
|
|
95
|
+
expect(verified).toBe(true);
|
|
96
|
+
|
|
97
|
+
expect(proof.publicOutput.stateTransitionsHash.toString()).toStrictEqual(
|
|
98
|
+
expectedStateTransitionsHash
|
|
99
|
+
);
|
|
100
|
+
expect(proof.publicOutput.status.toBoolean()).toBe(expectedStatus);
|
|
101
|
+
}, 180_000);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("getTotalSupply", () => {
|
|
105
|
+
beforeAll(createChain);
|
|
106
|
+
|
|
107
|
+
describe("state transitions", () => {
|
|
108
|
+
let stateTransitions: ProvableStateTransition[];
|
|
109
|
+
|
|
110
|
+
beforeEach(async () => {
|
|
111
|
+
const executionContext = container.resolve(
|
|
112
|
+
RuntimeMethodExecutionContext
|
|
113
|
+
);
|
|
114
|
+
executionContext.setup({
|
|
115
|
+
transaction: RuntimeTransaction.dummyTransaction(),
|
|
116
|
+
networkState: NetworkState.empty(),
|
|
117
|
+
});
|
|
118
|
+
await balances.getTotalSupply();
|
|
119
|
+
|
|
120
|
+
stateTransitions = executionContext
|
|
121
|
+
.current()
|
|
122
|
+
.result.stateTransitions.map((stateTransition) =>
|
|
123
|
+
stateTransition.toProvable()
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should return a single state transition", () => {
|
|
128
|
+
expect.assertions(1);
|
|
129
|
+
expect(stateTransitions).toHaveLength(1);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should have a state transition for the correct path", () => {
|
|
133
|
+
expect.assertions(1);
|
|
134
|
+
|
|
135
|
+
const path = Path.fromProperty("Balances", "totalSupply");
|
|
136
|
+
|
|
137
|
+
expect(stateTransitions[0].path.toString()).toStrictEqual(
|
|
138
|
+
path.toString()
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("should produce a from-only state transition", async () => {
|
|
143
|
+
expect.assertions(3);
|
|
144
|
+
|
|
145
|
+
const [stateTransition] = stateTransitions;
|
|
146
|
+
|
|
147
|
+
const value = UInt64.fromFields(
|
|
148
|
+
await getStateValue(balances.totalSupply.path)
|
|
149
|
+
);
|
|
150
|
+
const treeValue = Poseidon.hash(value.toFields());
|
|
151
|
+
|
|
152
|
+
expect(stateTransition.from.isSome.toBoolean()).toBe(true);
|
|
153
|
+
expect(stateTransition.from.value.toString()).toBe(
|
|
154
|
+
treeValue.toString()
|
|
155
|
+
);
|
|
156
|
+
expect(stateTransition.to.isSome.toBoolean()).toBe(false);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe("state transitions from empty state", () => {
|
|
161
|
+
let stateTransitions: ProvableStateTransition[];
|
|
162
|
+
|
|
163
|
+
beforeAll(async () => {
|
|
164
|
+
await createChain();
|
|
165
|
+
|
|
166
|
+
await state.set(balances.totalSupply.path!, undefined);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
beforeEach(async () => {
|
|
170
|
+
const executionContext = container.resolve(
|
|
171
|
+
RuntimeMethodExecutionContext
|
|
172
|
+
);
|
|
173
|
+
executionContext.setup({
|
|
174
|
+
transaction: RuntimeTransaction.dummyTransaction(),
|
|
175
|
+
networkState: NetworkState.empty(),
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
await balances.getTotalSupply();
|
|
179
|
+
|
|
180
|
+
stateTransitions = executionContext
|
|
181
|
+
.current()
|
|
182
|
+
.result.stateTransitions.map((stateTransition) =>
|
|
183
|
+
stateTransition.toProvable()
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("should return a single state transition", () => {
|
|
188
|
+
expect.assertions(1);
|
|
189
|
+
expect(stateTransitions).toHaveLength(1);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("should have a state transition for the correct path", () => {
|
|
193
|
+
expect.assertions(1);
|
|
194
|
+
|
|
195
|
+
const path = Path.fromProperty("Balances", "totalSupply");
|
|
196
|
+
|
|
197
|
+
expect(stateTransitions[0].path.toString()).toStrictEqual(
|
|
198
|
+
path.toString()
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("should produce a from-only state transition", () => {
|
|
203
|
+
expect.assertions(3);
|
|
204
|
+
|
|
205
|
+
const [stateTransition] = stateTransitions;
|
|
206
|
+
|
|
207
|
+
const treeValue = Field(0);
|
|
208
|
+
|
|
209
|
+
expect(stateTransition.from.isSome.toBoolean()).toBe(true);
|
|
210
|
+
expect(stateTransition.from.value.toString()).toBe(
|
|
211
|
+
treeValue.toString()
|
|
212
|
+
);
|
|
213
|
+
expect(stateTransition.to.isSome.toBoolean()).toBe(false);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe("setTotalSupply", () => {
|
|
219
|
+
beforeAll(createChain);
|
|
220
|
+
|
|
221
|
+
describe("state transitions", () => {
|
|
222
|
+
let stateTransitions: ProvableStateTransition[];
|
|
223
|
+
|
|
224
|
+
beforeEach(async () => {
|
|
225
|
+
const executionContext = container.resolve(
|
|
226
|
+
RuntimeMethodExecutionContext
|
|
227
|
+
);
|
|
228
|
+
executionContext.setup({
|
|
229
|
+
transaction: RuntimeTransaction.dummyTransaction(),
|
|
230
|
+
networkState: NetworkState.empty(),
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
await balances.setTotalSupply();
|
|
234
|
+
|
|
235
|
+
stateTransitions = executionContext
|
|
236
|
+
.current()
|
|
237
|
+
.result.stateTransitions.map((stateTransition) =>
|
|
238
|
+
stateTransition.toProvable()
|
|
239
|
+
);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it("should return a single state transition", () => {
|
|
243
|
+
expect.assertions(1);
|
|
244
|
+
expect(stateTransitions).toHaveLength(1);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("should have a state transition for the correct path", () => {
|
|
248
|
+
expect.assertions(1);
|
|
249
|
+
|
|
250
|
+
const path = Path.fromProperty("Balances", "totalSupply");
|
|
251
|
+
|
|
252
|
+
expect(stateTransitions[0].path.toString()).toStrictEqual(
|
|
253
|
+
path.toString()
|
|
254
|
+
);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("should produce a from-to state transition", async () => {
|
|
258
|
+
expect.assertions(4);
|
|
259
|
+
|
|
260
|
+
const [stateTransition] = stateTransitions;
|
|
261
|
+
const fromValue = UInt64.fromFields(
|
|
262
|
+
await getStateValue(balances.totalSupply.path)
|
|
263
|
+
);
|
|
264
|
+
const fromTreeValue = Poseidon.hash(fromValue.toFields());
|
|
265
|
+
|
|
266
|
+
const toValue = UInt64.from(20);
|
|
267
|
+
const toTreeValue = Poseidon.hash(toValue.toFields());
|
|
268
|
+
|
|
269
|
+
expect(stateTransition.from.isSome.toBoolean()).toBe(true);
|
|
270
|
+
expect(stateTransition.from.value.toString()).toBe(
|
|
271
|
+
fromTreeValue.toString()
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
expect(stateTransition.to.isSome.toBoolean()).toBe(true);
|
|
275
|
+
expect(stateTransition.to.value.toString()).toBe(
|
|
276
|
+
toTreeValue.toString()
|
|
277
|
+
);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
describe("getBalance", () => {
|
|
283
|
+
beforeAll(createChain);
|
|
284
|
+
|
|
285
|
+
describe("state transitions", () => {
|
|
286
|
+
let stateTransitions: ProvableStateTransition[];
|
|
287
|
+
const address = PrivateKey.random().toPublicKey();
|
|
288
|
+
|
|
289
|
+
beforeEach(async () => {
|
|
290
|
+
const executionContext = container.resolve(
|
|
291
|
+
RuntimeMethodExecutionContext
|
|
292
|
+
);
|
|
293
|
+
executionContext.setup({
|
|
294
|
+
transaction: RuntimeTransaction.dummyTransaction(),
|
|
295
|
+
networkState: NetworkState.empty(),
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
await balances.getBalance(address);
|
|
299
|
+
|
|
300
|
+
stateTransitions = executionContext
|
|
301
|
+
.current()
|
|
302
|
+
.result.stateTransitions.map((stateTransition) =>
|
|
303
|
+
stateTransition.toProvable()
|
|
304
|
+
);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("should return a single state transition", () => {
|
|
308
|
+
expect.assertions(1);
|
|
309
|
+
expect(stateTransitions).toHaveLength(1);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it("should have a state transition for the correct path", () => {
|
|
313
|
+
expect.assertions(1);
|
|
314
|
+
|
|
315
|
+
const path = Path.fromKey<PublicKey>(
|
|
316
|
+
Path.fromProperty("Balances", "balances"),
|
|
317
|
+
PublicKey,
|
|
318
|
+
address
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
expect(stateTransitions[0].path.toString()).toStrictEqual(
|
|
322
|
+
path.toString()
|
|
323
|
+
);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it("should produce a from-only state transition, for non-existing state", () => {
|
|
327
|
+
expect.assertions(3);
|
|
328
|
+
|
|
329
|
+
const [stateTransition] = stateTransitions;
|
|
330
|
+
|
|
331
|
+
expect(stateTransition.from.isSome.toBoolean()).toBe(true);
|
|
332
|
+
expect(stateTransition.from.value.toString()).toBe(Field(0).toString());
|
|
333
|
+
expect(stateTransition.to.isSome.toBoolean()).toBe(false);
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { PublicKey, UInt64 } from "o1js";
|
|
2
|
+
import { State, StateMap } from "@proto-kit/protocol";
|
|
3
|
+
import { Presets } from "@proto-kit/common";
|
|
4
|
+
|
|
5
|
+
import { RuntimeModule, runtimeMethod, runtimeModule, state } from "../../src";
|
|
6
|
+
|
|
7
|
+
import { Admin } from "./Admin.js";
|
|
8
|
+
|
|
9
|
+
interface BalancesConfig {}
|
|
10
|
+
|
|
11
|
+
@runtimeModule()
|
|
12
|
+
export class Balances extends RuntimeModule<BalancesConfig> {
|
|
13
|
+
/**
|
|
14
|
+
* We use `satisfies` here in order to be able to access
|
|
15
|
+
* presets by key in a type safe way.
|
|
16
|
+
*/
|
|
17
|
+
public static presets = {} satisfies Presets<BalancesConfig>;
|
|
18
|
+
|
|
19
|
+
@state() public totalSupply = State.from<UInt64>(UInt64);
|
|
20
|
+
|
|
21
|
+
@state() public balances = StateMap.from<PublicKey, UInt64>(
|
|
22
|
+
PublicKey,
|
|
23
|
+
UInt64
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
public constructor(public admin: Admin) {
|
|
27
|
+
super();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@runtimeMethod()
|
|
31
|
+
public async getTotalSupply() {
|
|
32
|
+
await this.totalSupply.get();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@runtimeMethod()
|
|
36
|
+
public async setTotalSupply() {
|
|
37
|
+
await this.totalSupply.set(UInt64.from(20));
|
|
38
|
+
await this.admin.isAdmin(this.transaction.sender.value);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@runtimeMethod()
|
|
42
|
+
public async getBalance(address: PublicKey) {
|
|
43
|
+
(await this.balances.get(address)).orElse(UInt64.zero);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@runtimeMethod()
|
|
47
|
+
public async transientState() {
|
|
48
|
+
const totalSupply = await this.totalSupply.get();
|
|
49
|
+
await this.totalSupply.set(totalSupply.orElse(UInt64.zero).add(100));
|
|
50
|
+
|
|
51
|
+
const totalSupply2 = await this.totalSupply.get();
|
|
52
|
+
await this.totalSupply.set(totalSupply2.orElse(UInt64.zero).add(100));
|
|
53
|
+
}
|
|
54
|
+
}
|