@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,154 @@
|
|
|
1
|
+
/* eslint-disable new-cap */
|
|
2
|
+
|
|
3
|
+
import { Mixin } from "ts-mixer";
|
|
4
|
+
import { Bool, Field, Provable, type FlexibleProvablePure } from "snarkyjs";
|
|
5
|
+
import { container } from "tsyringe";
|
|
6
|
+
import { Option, StateTransition, type Path } from "@proto-kit/protocol";
|
|
7
|
+
|
|
8
|
+
import { PartialRuntime } from "../runtime/RuntimeModule.js";
|
|
9
|
+
import { RuntimeMethodExecutionContext } from "../method/RuntimeMethodExecutionContext.js";
|
|
10
|
+
|
|
11
|
+
export class WithPath {
|
|
12
|
+
public path?: Field;
|
|
13
|
+
|
|
14
|
+
public hasPathOrFail(): asserts this is { path: Path } {
|
|
15
|
+
if (!this.path) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
"Could not find 'path', did you forget to add '@state' to your state property?"
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class WithRuntime {
|
|
24
|
+
public runtime?: PartialRuntime;
|
|
25
|
+
|
|
26
|
+
public hasRuntimeOrFail(): asserts this is {
|
|
27
|
+
runtime: PartialRuntime;
|
|
28
|
+
} {
|
|
29
|
+
if (!this.runtime) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"Could not find 'runtime', did you forget to add '@state' to your state property?"
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Utilities for runtime module state, such as get/set
|
|
39
|
+
*/
|
|
40
|
+
export class State<Value> extends Mixin(WithPath, WithRuntime) {
|
|
41
|
+
/**
|
|
42
|
+
* Creates a new state wrapper for the provided value type.
|
|
43
|
+
*
|
|
44
|
+
* @param valueType - Type of value to be stored (e.g. UInt64, Struct, ...)
|
|
45
|
+
* @returns New state for the given value type.
|
|
46
|
+
*/
|
|
47
|
+
public static from<Value>(valueType: FlexibleProvablePure<Value>) {
|
|
48
|
+
return new State<Value>(valueType);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Computes a dummy value for the given value type.
|
|
53
|
+
*
|
|
54
|
+
* @param valueType - Value type to generate the dummy value for
|
|
55
|
+
* @returns Dummy value for the given value type
|
|
56
|
+
*/
|
|
57
|
+
public static dummyValue<Value>(
|
|
58
|
+
valueType: FlexibleProvablePure<Value>
|
|
59
|
+
): Value {
|
|
60
|
+
const length = valueType.sizeInFields();
|
|
61
|
+
const fields = Array.from({ length }, () => Field(0));
|
|
62
|
+
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
64
|
+
return valueType.fromFields(fields) as Value;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public constructor(public valueType: FlexibleProvablePure<Value>) {
|
|
68
|
+
super();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Provides an in-circuit witness for the current state representation,
|
|
73
|
+
* and constructs an Option out of it.
|
|
74
|
+
*
|
|
75
|
+
* @returns Optional value of the current state
|
|
76
|
+
*/
|
|
77
|
+
private witnessState() {
|
|
78
|
+
// get the value from storage, or return a dummy value instead
|
|
79
|
+
const value = Provable.witness(this.valueType, () => {
|
|
80
|
+
this.hasRuntimeOrFail();
|
|
81
|
+
this.hasPathOrFail();
|
|
82
|
+
|
|
83
|
+
const fields = this.runtime.stateService.get(this.path);
|
|
84
|
+
if (fields) {
|
|
85
|
+
// eslint-disable-next-line max-len
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
87
|
+
return this.valueType.fromFields(fields) as Value;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return State.dummyValue(this.valueType);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// check if the value exists in the storage or not
|
|
94
|
+
const isSome = Provable.witness(Bool, () => {
|
|
95
|
+
this.hasRuntimeOrFail();
|
|
96
|
+
this.hasPathOrFail();
|
|
97
|
+
|
|
98
|
+
const fields = this.runtime.stateService.get(this.path);
|
|
99
|
+
|
|
100
|
+
return Bool(fields !== undefined);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return Option.from(isSome, value, this.valueType);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Retrieves the current state and creates a state transition
|
|
108
|
+
* anchoring the use of the current state value in the circuit.
|
|
109
|
+
*
|
|
110
|
+
* @returns Option representation of the current state.
|
|
111
|
+
*/
|
|
112
|
+
public get(): Option<Value> {
|
|
113
|
+
const option = this.witnessState();
|
|
114
|
+
|
|
115
|
+
this.hasPathOrFail();
|
|
116
|
+
|
|
117
|
+
const stateTransition = StateTransition.from(this.path, option);
|
|
118
|
+
|
|
119
|
+
container
|
|
120
|
+
.resolve(RuntimeMethodExecutionContext)
|
|
121
|
+
.addStateTransition(stateTransition);
|
|
122
|
+
|
|
123
|
+
return option;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Sets a new state value by creating a state transition from
|
|
128
|
+
* the current value to the newly set value.
|
|
129
|
+
*
|
|
130
|
+
* The newly set value isn't available via state.get(), since the
|
|
131
|
+
* state transitions are not applied within the same circuit.
|
|
132
|
+
* You can however store and access your new value in
|
|
133
|
+
* a separate circuit variable.
|
|
134
|
+
*
|
|
135
|
+
* @param value - Value to be set as the current state
|
|
136
|
+
*/
|
|
137
|
+
public set(value: Value) {
|
|
138
|
+
// link the transition to the current state
|
|
139
|
+
const fromOption = this.witnessState();
|
|
140
|
+
const toOption = Option.from(Bool(true), value, this.valueType);
|
|
141
|
+
|
|
142
|
+
this.hasPathOrFail();
|
|
143
|
+
|
|
144
|
+
const stateTransition = StateTransition.fromTo(
|
|
145
|
+
this.path,
|
|
146
|
+
fromOption,
|
|
147
|
+
toOption
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
container
|
|
151
|
+
.resolve(RuntimeMethodExecutionContext)
|
|
152
|
+
.addStateTransition(stateTransition);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Field, FlexibleProvablePure } from "snarkyjs";
|
|
2
|
+
import { Path, type Option } from "@proto-kit/protocol";
|
|
3
|
+
import { Mixin } from "ts-mixer";
|
|
4
|
+
|
|
5
|
+
import { State, WithRuntime, WithPath } from "./State.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Map-like wrapper for state
|
|
9
|
+
*/
|
|
10
|
+
// eslint-disable-next-line new-cap
|
|
11
|
+
export class StateMap<KeyType, ValueType> extends Mixin(WithPath, WithRuntime) {
|
|
12
|
+
/**
|
|
13
|
+
* Create a new state map with the given key and value types
|
|
14
|
+
*
|
|
15
|
+
* @param keyType - Type to be used as a key
|
|
16
|
+
* @param valueType - Type to be stored as a value
|
|
17
|
+
* @returns State map with provided key and value types.
|
|
18
|
+
*/
|
|
19
|
+
public static from<KeyType, ValueType>(
|
|
20
|
+
keyType: FlexibleProvablePure<KeyType>,
|
|
21
|
+
valueType: FlexibleProvablePure<ValueType>
|
|
22
|
+
) {
|
|
23
|
+
return new StateMap<KeyType, ValueType>(keyType, valueType);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public constructor(
|
|
27
|
+
public keyType: FlexibleProvablePure<KeyType>,
|
|
28
|
+
public valueType: FlexibleProvablePure<ValueType>
|
|
29
|
+
) {
|
|
30
|
+
super();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public getPath(key: KeyType): Field {
|
|
34
|
+
this.hasPathOrFail();
|
|
35
|
+
return Path.fromKey(this.path, this.keyType, key);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Obtains a value for the provided key in the current state map.
|
|
40
|
+
*
|
|
41
|
+
* @param key - Key to obtain the state for
|
|
42
|
+
* @returns Value for the provided key.
|
|
43
|
+
*/
|
|
44
|
+
public get(key: KeyType): Option<ValueType> {
|
|
45
|
+
const state = State.from(this.valueType);
|
|
46
|
+
this.hasPathOrFail();
|
|
47
|
+
this.hasRuntimeOrFail();
|
|
48
|
+
|
|
49
|
+
state.path = this.getPath(key);
|
|
50
|
+
state.runtime = this.runtime;
|
|
51
|
+
return state.get();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Sets a value for the given key in the current state map.
|
|
56
|
+
*
|
|
57
|
+
* @param key - Key to store the value under
|
|
58
|
+
* @param value - Value to be stored under the given key
|
|
59
|
+
*/
|
|
60
|
+
public set(key: KeyType, value: ValueType) {
|
|
61
|
+
const state = State.from(this.valueType);
|
|
62
|
+
this.hasPathOrFail();
|
|
63
|
+
this.hasRuntimeOrFail();
|
|
64
|
+
|
|
65
|
+
state.path = Path.fromKey(this.path, this.keyType, key);
|
|
66
|
+
state.runtime = this.runtime;
|
|
67
|
+
state.set(value);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { inject, injectable } from "tsyringe";
|
|
2
|
+
|
|
3
|
+
import { StateService } from "./InMemoryStateService";
|
|
4
|
+
|
|
5
|
+
@injectable()
|
|
6
|
+
export class StateServiceProvider {
|
|
7
|
+
private readonly defaultStateService: StateService = this.currentStateService;
|
|
8
|
+
|
|
9
|
+
public constructor(
|
|
10
|
+
@inject("StateService") private currentStateService: StateService
|
|
11
|
+
) {}
|
|
12
|
+
|
|
13
|
+
public get stateService(): StateService {
|
|
14
|
+
return this.currentStateService;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public setCurrentStateService(service: StateService) {
|
|
18
|
+
this.currentStateService = service;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public resetToDefault() {
|
|
22
|
+
this.currentStateService = this.defaultStateService;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Path } from "@proto-kit/protocol";
|
|
2
|
+
|
|
3
|
+
import type { RuntimeModule } from "../runtime/RuntimeModule.js";
|
|
4
|
+
|
|
5
|
+
import type { State } from "./State.js";
|
|
6
|
+
|
|
7
|
+
const errors = {
|
|
8
|
+
missingName: (className: string) =>
|
|
9
|
+
new Error(
|
|
10
|
+
`Unable to provide a unique identifier for state, ${className} is missing a name.
|
|
11
|
+
Did you forget to extend your runtime module with 'extends RuntimeModule'?`
|
|
12
|
+
),
|
|
13
|
+
|
|
14
|
+
missingRuntime: (className: string) =>
|
|
15
|
+
new Error(
|
|
16
|
+
`Unable to provide 'runtime' for state, ${className} is missing a name.
|
|
17
|
+
Did you forget to extend your runtime module with 'extends RuntimeModule'?`
|
|
18
|
+
),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Decorates a runtime module property as state, passing down some
|
|
23
|
+
* underlying values to improve developer experience.
|
|
24
|
+
*/
|
|
25
|
+
export function state() {
|
|
26
|
+
return <TargetRuntimeModule extends RuntimeModule<unknown>>(
|
|
27
|
+
target: TargetRuntimeModule,
|
|
28
|
+
propertyKey: string
|
|
29
|
+
) => {
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
31
|
+
let value: State<unknown> | undefined;
|
|
32
|
+
|
|
33
|
+
Object.defineProperty(target, propertyKey, {
|
|
34
|
+
enumerable: true,
|
|
35
|
+
|
|
36
|
+
get: function get() {
|
|
37
|
+
// eslint-disable-next-line max-len
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
39
|
+
const self = this as TargetRuntimeModule;
|
|
40
|
+
|
|
41
|
+
if (self.name === undefined) {
|
|
42
|
+
throw errors.missingName(self.constructor.name);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!self.runtime) {
|
|
46
|
+
throw errors.missingRuntime(self.constructor.name);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const path = Path.fromProperty(self.name, propertyKey);
|
|
50
|
+
if (value) {
|
|
51
|
+
value.path = path;
|
|
52
|
+
// eslint-disable-next-line no-warning-comments
|
|
53
|
+
// TODO: why is this complaining about `any`?
|
|
54
|
+
|
|
55
|
+
value.runtime = self.runtime;
|
|
56
|
+
}
|
|
57
|
+
return value;
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
set: (newValue: State<unknown>) => {
|
|
61
|
+
value = newValue;
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { InMemoryStateService, Runtime } from "../src";
|
|
2
|
+
import { Balances } from "./modules/Balances";
|
|
3
|
+
import { Bool } from "snarkyjs";
|
|
4
|
+
|
|
5
|
+
describe("runtime", () => {
|
|
6
|
+
|
|
7
|
+
it("should encode methodnames correctly", () => {
|
|
8
|
+
const runtime = Runtime.from({
|
|
9
|
+
state: new InMemoryStateService(),
|
|
10
|
+
modules: {
|
|
11
|
+
Balances: Balances
|
|
12
|
+
},
|
|
13
|
+
config: {
|
|
14
|
+
Balances: {
|
|
15
|
+
test: Bool(true)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const balances = runtime.resolve("Balances")
|
|
21
|
+
expect(balances).toBeDefined();
|
|
22
|
+
|
|
23
|
+
console.log(Object.keys(balances));
|
|
24
|
+
console.log(balances.getTotalSupply);
|
|
25
|
+
|
|
26
|
+
const moduleName = "Balances";
|
|
27
|
+
const methodName = "getTotalSupply";
|
|
28
|
+
|
|
29
|
+
const methodId = runtime.getMethodId(moduleName, methodName);
|
|
30
|
+
const method = runtime.getMethodById(methodId)
|
|
31
|
+
|
|
32
|
+
// eslint-disable-next-line jest/no-restricted-matchers
|
|
33
|
+
expect(method).toBeDefined();
|
|
34
|
+
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PublicKey } from "snarkyjs";
|
|
2
|
+
|
|
3
|
+
import { runtimeModule } from "../../src/module/decorator.js";
|
|
4
|
+
import { RuntimeModule } from "../../src/runtime/RuntimeModule.js";
|
|
5
|
+
import { runtimeMethod } from "../../src/method/runtimeMethod.js";
|
|
6
|
+
import { assert } from "../../src/method/assert.js";
|
|
7
|
+
|
|
8
|
+
interface AdminConfig {
|
|
9
|
+
publicKey: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@runtimeModule()
|
|
13
|
+
export class Admin extends RuntimeModule<AdminConfig> {
|
|
14
|
+
@runtimeMethod()
|
|
15
|
+
public isAdmin(publicKey: PublicKey) {
|
|
16
|
+
const admin = PublicKey.empty().toConstant();
|
|
17
|
+
assert(admin.equals(publicKey));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/* eslint-disable max-lines */
|
|
2
|
+
import "reflect-metadata";
|
|
3
|
+
import {
|
|
4
|
+
Bool,
|
|
5
|
+
Field,
|
|
6
|
+
Poseidon,
|
|
7
|
+
PrivateKey,
|
|
8
|
+
Proof,
|
|
9
|
+
PublicKey,
|
|
10
|
+
UInt64,
|
|
11
|
+
} from "snarkyjs";
|
|
12
|
+
import { container } from "tsyringe";
|
|
13
|
+
import { type ProvableStateTransition, Path } from "@proto-kit/protocol";
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
InMemoryStateService,
|
|
17
|
+
StateService,
|
|
18
|
+
} from "../../src/state/InMemoryStateService.js";
|
|
19
|
+
import {
|
|
20
|
+
MethodPublicOutput,
|
|
21
|
+
Runtime,
|
|
22
|
+
RuntimeMethodExecutionContext,
|
|
23
|
+
} from "../../src";
|
|
24
|
+
|
|
25
|
+
import { Balances } from "./Balances.js";
|
|
26
|
+
import { Admin } from "./Admin.js";
|
|
27
|
+
|
|
28
|
+
describe("balances", () => {
|
|
29
|
+
let balances: Balances;
|
|
30
|
+
|
|
31
|
+
let state: StateService;
|
|
32
|
+
|
|
33
|
+
let runtime: Runtime<{
|
|
34
|
+
Admin: typeof Admin;
|
|
35
|
+
Balances: typeof Balances;
|
|
36
|
+
}>;
|
|
37
|
+
|
|
38
|
+
function getStateValue(path: Field | undefined) {
|
|
39
|
+
if (!path) {
|
|
40
|
+
throw new Error("Path not found");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const stateValue = state.get(path);
|
|
44
|
+
|
|
45
|
+
if (!stateValue) {
|
|
46
|
+
throw new Error("stateValue is undefined");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return stateValue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createChain() {
|
|
53
|
+
state = new InMemoryStateService();
|
|
54
|
+
|
|
55
|
+
runtime = Runtime.from({
|
|
56
|
+
state,
|
|
57
|
+
|
|
58
|
+
modules: {
|
|
59
|
+
Admin,
|
|
60
|
+
Balances,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
runtime.appChain = {
|
|
65
|
+
areProofsEnabled: false,
|
|
66
|
+
|
|
67
|
+
setProofsEnabled(areProofsEnabled) {
|
|
68
|
+
this.areProofsEnabled = areProofsEnabled;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
runtime.configure({
|
|
73
|
+
Admin: {
|
|
74
|
+
publicKey: PublicKey.empty().toBase58(),
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
Balances: {
|
|
78
|
+
test: Bool(true),
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
balances = runtime.resolve("Balances");
|
|
83
|
+
|
|
84
|
+
state.set(
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
86
|
+
balances.totalSupply.path!,
|
|
87
|
+
UInt64.from(10).toFields()
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
describe("compile and prove", () => {
|
|
92
|
+
beforeAll(createChain);
|
|
93
|
+
|
|
94
|
+
// Disabled until we implement a mechanism to enable/disable compiling tests
|
|
95
|
+
it("should compile and prove a method execution", async () => {
|
|
96
|
+
expect.assertions(3);
|
|
97
|
+
|
|
98
|
+
runtime.zkProgrammable.appChain?.setProofsEnabled(true);
|
|
99
|
+
|
|
100
|
+
const executionContext = container.resolve(RuntimeMethodExecutionContext);
|
|
101
|
+
const expectedStateTransitionsHash =
|
|
102
|
+
"1439144406936083177718146178121957896974210157062549589517697792374542035761";
|
|
103
|
+
const expectedStatus = true;
|
|
104
|
+
|
|
105
|
+
await runtime.zkProgrammable.zkProgram.compile();
|
|
106
|
+
|
|
107
|
+
balances.getTotalSupply();
|
|
108
|
+
|
|
109
|
+
const { result } = executionContext.current();
|
|
110
|
+
|
|
111
|
+
const proof = await result.prove<Proof<undefined, MethodPublicOutput>>();
|
|
112
|
+
|
|
113
|
+
const verified = await runtime.zkProgrammable.zkProgram.verify(proof);
|
|
114
|
+
|
|
115
|
+
runtime.zkProgrammable.appChain?.setProofsEnabled(false);
|
|
116
|
+
|
|
117
|
+
expect(verified).toBe(true);
|
|
118
|
+
|
|
119
|
+
expect(proof.publicOutput.stateTransitionsHash.toString()).toStrictEqual(
|
|
120
|
+
expectedStateTransitionsHash
|
|
121
|
+
);
|
|
122
|
+
expect(proof.publicOutput.status.toBoolean()).toBe(expectedStatus);
|
|
123
|
+
}, 180_000);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe("getTotalSupply", () => {
|
|
127
|
+
beforeAll(createChain);
|
|
128
|
+
|
|
129
|
+
describe("state transitions", () => {
|
|
130
|
+
let stateTransitions: ProvableStateTransition[];
|
|
131
|
+
|
|
132
|
+
beforeEach(() => {
|
|
133
|
+
const executionContext = container.resolve(
|
|
134
|
+
RuntimeMethodExecutionContext
|
|
135
|
+
);
|
|
136
|
+
balances.getTotalSupply();
|
|
137
|
+
|
|
138
|
+
stateTransitions = executionContext
|
|
139
|
+
.current()
|
|
140
|
+
.result.stateTransitions.map((stateTransition) =>
|
|
141
|
+
stateTransition.toProvable()
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("should return a single state transition", () => {
|
|
146
|
+
expect.assertions(1);
|
|
147
|
+
expect(stateTransitions).toHaveLength(1);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("should have a state transition for the correct path", () => {
|
|
151
|
+
expect.assertions(1);
|
|
152
|
+
|
|
153
|
+
const path = Path.fromProperty("Balances", "totalSupply");
|
|
154
|
+
|
|
155
|
+
expect(stateTransitions[0].path.toString()).toStrictEqual(
|
|
156
|
+
path.toString()
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should produce a from-only state transition", () => {
|
|
161
|
+
expect.assertions(3);
|
|
162
|
+
|
|
163
|
+
const [stateTransition] = stateTransitions;
|
|
164
|
+
|
|
165
|
+
const value = UInt64.fromFields(
|
|
166
|
+
getStateValue(balances.totalSupply.path)
|
|
167
|
+
);
|
|
168
|
+
const treeValue = Poseidon.hash(value.toFields());
|
|
169
|
+
|
|
170
|
+
expect(stateTransition.from.isSome.toBoolean()).toBe(true);
|
|
171
|
+
expect(stateTransition.from.value.toString()).toBe(
|
|
172
|
+
treeValue.toString()
|
|
173
|
+
);
|
|
174
|
+
expect(stateTransition.to.isSome.toBoolean()).toBe(false);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe("state transitions from empty state", () => {
|
|
179
|
+
let stateTransitions: ProvableStateTransition[];
|
|
180
|
+
|
|
181
|
+
beforeAll(() => {
|
|
182
|
+
createChain();
|
|
183
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
184
|
+
state.set(balances.totalSupply.path!, undefined);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
beforeEach(() => {
|
|
188
|
+
const executionContext = container.resolve(
|
|
189
|
+
RuntimeMethodExecutionContext
|
|
190
|
+
);
|
|
191
|
+
balances.getTotalSupply();
|
|
192
|
+
|
|
193
|
+
stateTransitions = executionContext
|
|
194
|
+
.current()
|
|
195
|
+
.result.stateTransitions.map((stateTransition) =>
|
|
196
|
+
stateTransition.toProvable()
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("should return a single state transition", () => {
|
|
201
|
+
expect.assertions(1);
|
|
202
|
+
expect(stateTransitions).toHaveLength(1);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("should have a state transition for the correct path", () => {
|
|
206
|
+
expect.assertions(1);
|
|
207
|
+
|
|
208
|
+
const path = Path.fromProperty("Balances", "totalSupply");
|
|
209
|
+
|
|
210
|
+
expect(stateTransitions[0].path.toString()).toStrictEqual(
|
|
211
|
+
path.toString()
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("should produce a from-only state transition", () => {
|
|
216
|
+
expect.assertions(3);
|
|
217
|
+
|
|
218
|
+
const [stateTransition] = stateTransitions;
|
|
219
|
+
|
|
220
|
+
const treeValue = Field(0);
|
|
221
|
+
|
|
222
|
+
expect(stateTransition.from.isSome.toBoolean()).toBe(true);
|
|
223
|
+
expect(stateTransition.from.value.toString()).toBe(
|
|
224
|
+
treeValue.toString()
|
|
225
|
+
);
|
|
226
|
+
expect(stateTransition.to.isSome.toBoolean()).toBe(false);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe("setTotalSupply", () => {
|
|
232
|
+
beforeAll(createChain);
|
|
233
|
+
|
|
234
|
+
describe("state transitions", () => {
|
|
235
|
+
let stateTransitions: ProvableStateTransition[];
|
|
236
|
+
|
|
237
|
+
beforeEach(() => {
|
|
238
|
+
const executionContext = container.resolve(
|
|
239
|
+
RuntimeMethodExecutionContext
|
|
240
|
+
);
|
|
241
|
+
balances.setTotalSupply();
|
|
242
|
+
|
|
243
|
+
stateTransitions = executionContext
|
|
244
|
+
.current()
|
|
245
|
+
.result.stateTransitions.map((stateTransition) =>
|
|
246
|
+
stateTransition.toProvable()
|
|
247
|
+
);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("should return a single state transition", () => {
|
|
251
|
+
expect.assertions(1);
|
|
252
|
+
expect(stateTransitions).toHaveLength(1);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("should have a state transition for the correct path", () => {
|
|
256
|
+
expect.assertions(1);
|
|
257
|
+
|
|
258
|
+
const path = Path.fromProperty("Balances", "totalSupply");
|
|
259
|
+
|
|
260
|
+
expect(stateTransitions[0].path.toString()).toStrictEqual(
|
|
261
|
+
path.toString()
|
|
262
|
+
);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("should produce a from-to state transition", () => {
|
|
266
|
+
expect.assertions(4);
|
|
267
|
+
|
|
268
|
+
const [stateTransition] = stateTransitions;
|
|
269
|
+
const fromValue = UInt64.fromFields(
|
|
270
|
+
getStateValue(balances.totalSupply.path)
|
|
271
|
+
);
|
|
272
|
+
const fromTreeValue = Poseidon.hash(fromValue.toFields());
|
|
273
|
+
|
|
274
|
+
const toValue = UInt64.from(20);
|
|
275
|
+
const toTreeValue = Poseidon.hash(toValue.toFields());
|
|
276
|
+
|
|
277
|
+
expect(stateTransition.from.isSome.toBoolean()).toBe(true);
|
|
278
|
+
expect(stateTransition.from.value.toString()).toBe(
|
|
279
|
+
fromTreeValue.toString()
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
expect(stateTransition.to.isSome.toBoolean()).toBe(true);
|
|
283
|
+
expect(stateTransition.to.value.toString()).toBe(
|
|
284
|
+
toTreeValue.toString()
|
|
285
|
+
);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
describe("getBalance", () => {
|
|
291
|
+
beforeAll(createChain);
|
|
292
|
+
|
|
293
|
+
describe("state transitions", () => {
|
|
294
|
+
let stateTransitions: ProvableStateTransition[];
|
|
295
|
+
const address = PrivateKey.random().toPublicKey();
|
|
296
|
+
|
|
297
|
+
beforeEach(() => {
|
|
298
|
+
const executionContext = container.resolve(
|
|
299
|
+
RuntimeMethodExecutionContext
|
|
300
|
+
);
|
|
301
|
+
balances.getBalance(address);
|
|
302
|
+
|
|
303
|
+
stateTransitions = executionContext
|
|
304
|
+
.current()
|
|
305
|
+
.result.stateTransitions.map((stateTransition) =>
|
|
306
|
+
stateTransition.toProvable()
|
|
307
|
+
);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it("should return a single state transition", () => {
|
|
311
|
+
expect.assertions(1);
|
|
312
|
+
expect(stateTransitions).toHaveLength(1);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("should have a state transition for the correct path", () => {
|
|
316
|
+
expect.assertions(1);
|
|
317
|
+
|
|
318
|
+
const path = Path.fromKey<PublicKey>(
|
|
319
|
+
Path.fromProperty("Balances", "balances"),
|
|
320
|
+
PublicKey,
|
|
321
|
+
address
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
expect(stateTransitions[0].path.toString()).toStrictEqual(
|
|
325
|
+
path.toString()
|
|
326
|
+
);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it("should produce a from-only state transition, for non-existing state", () => {
|
|
330
|
+
expect.assertions(3);
|
|
331
|
+
|
|
332
|
+
const [stateTransition] = stateTransitions;
|
|
333
|
+
|
|
334
|
+
expect(stateTransition.from.isSome.toBoolean()).toBe(true);
|
|
335
|
+
expect(stateTransition.from.value.toString()).toBe(Field(0).toString());
|
|
336
|
+
expect(stateTransition.to.isSome.toBoolean()).toBe(false);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
});
|