@proto-kit/protocol 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.
Files changed (150) hide show
  1. package/LICENSE.md +201 -0
  2. package/README.md +45 -0
  3. package/dist/Constants.d.ts +4 -0
  4. package/dist/Constants.d.ts.map +1 -0
  5. package/dist/Constants.js +3 -0
  6. package/dist/config/ConfigurableModule.d.ts +18 -0
  7. package/dist/config/ConfigurableModule.d.ts.map +1 -0
  8. package/dist/config/ConfigurableModule.js +20 -0
  9. package/dist/config/ConfigurationAggregator.d.ts +10 -0
  10. package/dist/config/ConfigurationAggregator.d.ts.map +1 -0
  11. package/dist/config/ConfigurationAggregator.js +35 -0
  12. package/dist/config/ConfigurationReceiver.d.ts +25 -0
  13. package/dist/config/ConfigurationReceiver.d.ts.map +1 -0
  14. package/dist/config/ConfigurationReceiver.js +36 -0
  15. package/dist/config/ModuleContainer.d.ts +44 -0
  16. package/dist/config/ModuleContainer.d.ts.map +1 -0
  17. package/dist/config/ModuleContainer.js +89 -0
  18. package/dist/config/types.d.ts +2 -0
  19. package/dist/config/types.d.ts.map +1 -0
  20. package/dist/config/types.js +1 -0
  21. package/dist/index.d.ts +24 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +23 -0
  24. package/dist/model/MethodPublicInput.d.ts +51 -0
  25. package/dist/model/MethodPublicInput.d.ts.map +1 -0
  26. package/dist/model/MethodPublicInput.js +11 -0
  27. package/dist/model/MethodPublicOutput.d.ts +59 -0
  28. package/dist/model/MethodPublicOutput.d.ts.map +1 -0
  29. package/dist/model/MethodPublicOutput.js +12 -0
  30. package/dist/model/Option.d.ts +94 -0
  31. package/dist/model/Option.d.ts.map +1 -0
  32. package/dist/model/Option.js +96 -0
  33. package/dist/model/Path.d.ts +31 -0
  34. package/dist/model/Path.d.ts.map +1 -0
  35. package/dist/model/Path.js +44 -0
  36. package/dist/model/StateTransition.d.ts +85 -0
  37. package/dist/model/StateTransition.d.ts.map +1 -0
  38. package/dist/model/StateTransition.js +58 -0
  39. package/dist/model/StateTransitionProvableBatch.d.ts +57 -0
  40. package/dist/model/StateTransitionProvableBatch.d.ts.map +1 -0
  41. package/dist/model/StateTransitionProvableBatch.js +21 -0
  42. package/dist/model/network/NetworkState.d.ts +64 -0
  43. package/dist/model/network/NetworkState.d.ts.map +1 -0
  44. package/dist/model/network/NetworkState.js +14 -0
  45. package/dist/model/transaction/ProtocolTransaction.d.ts +70 -0
  46. package/dist/model/transaction/ProtocolTransaction.d.ts.map +1 -0
  47. package/dist/model/transaction/ProtocolTransaction.js +18 -0
  48. package/dist/model/transaction/RuntimeTransaction.d.ts +55 -0
  49. package/dist/model/transaction/RuntimeTransaction.d.ts.map +1 -0
  50. package/dist/model/transaction/RuntimeTransaction.js +26 -0
  51. package/dist/protocol/Protocol.d.ts +37 -0
  52. package/dist/protocol/Protocol.d.ts.map +1 -0
  53. package/dist/protocol/Protocol.js +50 -0
  54. package/dist/protocol/ProtocolModule.d.ts +9 -0
  55. package/dist/protocol/ProtocolModule.d.ts.map +1 -0
  56. package/dist/protocol/ProtocolModule.js +10 -0
  57. package/dist/prover/block/BlockProvable.d.ts +149 -0
  58. package/dist/prover/block/BlockProvable.d.ts.map +1 -0
  59. package/dist/prover/block/BlockProvable.js +20 -0
  60. package/dist/prover/block/BlockProver.d.ts +48 -0
  61. package/dist/prover/block/BlockProver.d.ts.map +1 -0
  62. package/dist/prover/block/BlockProver.js +171 -0
  63. package/dist/prover/block/BlockScopedModule.d.ts +3 -0
  64. package/dist/prover/block/BlockScopedModule.d.ts.map +1 -0
  65. package/dist/prover/block/BlockScopedModule.js +6 -0
  66. package/dist/prover/statetransition/StateTransitionProvable.d.ts +84 -0
  67. package/dist/prover/statetransition/StateTransitionProvable.d.ts.map +1 -0
  68. package/dist/prover/statetransition/StateTransitionProvable.js +11 -0
  69. package/dist/prover/statetransition/StateTransitionProver.d.ts +39 -0
  70. package/dist/prover/statetransition/StateTransitionProver.d.ts.map +1 -0
  71. package/dist/prover/statetransition/StateTransitionProver.js +157 -0
  72. package/dist/prover/statetransition/StateTransitionWitnessProvider.d.ts +16 -0
  73. package/dist/prover/statetransition/StateTransitionWitnessProvider.d.ts.map +1 -0
  74. package/dist/prover/statetransition/StateTransitionWitnessProvider.js +17 -0
  75. package/dist/prover/statetransition/StateTransitionWitnessProviderReference.d.ts +7 -0
  76. package/dist/prover/statetransition/StateTransitionWitnessProviderReference.d.ts.map +1 -0
  77. package/dist/prover/statetransition/StateTransitionWitnessProviderReference.js +20 -0
  78. package/dist/src/model/Option.d.ts +158 -0
  79. package/dist/src/model/Option.d.ts.map +1 -0
  80. package/dist/src/model/Option.js +53 -0
  81. package/dist/src/model/Path.d.ts +35 -0
  82. package/dist/src/model/Path.d.ts.map +1 -0
  83. package/dist/src/model/Path.js +51 -0
  84. package/dist/src/model/StateTransition.d.ts +201 -0
  85. package/dist/src/model/StateTransition.d.ts.map +1 -0
  86. package/dist/src/model/StateTransition.js +43 -0
  87. package/dist/src/utils/PrefixedHashList.d.ts +15 -0
  88. package/dist/src/utils/PrefixedHashList.d.ts.map +1 -0
  89. package/dist/src/utils/PrefixedHashList.js +28 -0
  90. package/dist/src/utils/ProvableHashList.d.ts +30 -0
  91. package/dist/src/utils/ProvableHashList.d.ts.map +1 -0
  92. package/dist/src/utils/ProvableHashList.js +43 -0
  93. package/dist/utils/PrefixedHashList.d.ts +14 -0
  94. package/dist/utils/PrefixedHashList.d.ts.map +1 -0
  95. package/dist/utils/PrefixedHashList.js +12 -0
  96. package/dist/utils/PrefixedProvableHashList.d.ts +8 -0
  97. package/dist/utils/PrefixedProvableHashList.d.ts.map +1 -0
  98. package/dist/utils/PrefixedProvableHashList.js +12 -0
  99. package/dist/utils/ProvableHashList.d.ts +27 -0
  100. package/dist/utils/ProvableHashList.d.ts.map +1 -0
  101. package/dist/utils/ProvableHashList.js +43 -0
  102. package/dist/utils/Utils.d.ts +17 -0
  103. package/dist/utils/Utils.d.ts.map +1 -0
  104. package/dist/utils/Utils.js +63 -0
  105. package/dist/utils/merkletree/InMemoryMerkleTreeStorage.d.ts +25 -0
  106. package/dist/utils/merkletree/InMemoryMerkleTreeStorage.d.ts.map +1 -0
  107. package/dist/utils/merkletree/InMemoryMerkleTreeStorage.js +72 -0
  108. package/dist/utils/merkletree/MemoryMerkleTreeStorage.d.ts +26 -0
  109. package/dist/utils/merkletree/MemoryMerkleTreeStorage.d.ts.map +1 -0
  110. package/dist/utils/merkletree/MemoryMerkleTreeStorage.js +79 -0
  111. package/dist/utils/merkletree/MerkleTreeStore.d.ts +11 -0
  112. package/dist/utils/merkletree/MerkleTreeStore.d.ts.map +1 -0
  113. package/dist/utils/merkletree/MerkleTreeStore.js +1 -0
  114. package/dist/utils/merkletree/RollupMerkleTree.d.ts +130 -0
  115. package/dist/utils/merkletree/RollupMerkleTree.d.ts.map +1 -0
  116. package/dist/utils/merkletree/RollupMerkleTree.js +244 -0
  117. package/jest.config.cjs +1 -0
  118. package/package.json +35 -0
  119. package/src/Constants.ts +3 -0
  120. package/src/index.ts +23 -0
  121. package/src/model/MethodPublicOutput.ts +12 -0
  122. package/src/model/Option.test.ts +21 -0
  123. package/src/model/Option.ts +133 -0
  124. package/src/model/Path.ts +52 -0
  125. package/src/model/StateTransition.ts +72 -0
  126. package/src/model/StateTransitionProvableBatch.ts +31 -0
  127. package/src/model/Transaction.ts +29 -0
  128. package/src/model/network/NetworkState.ts +15 -0
  129. package/src/model/transaction/ProtocolTransaction.ts +25 -0
  130. package/src/model/transaction/RuntimeTransaction.ts +34 -0
  131. package/src/protocol/Protocol.ts +129 -0
  132. package/src/protocol/ProtocolModule.ts +27 -0
  133. package/src/prover/block/BlockProvable.ts +45 -0
  134. package/src/prover/block/BlockProver.ts +302 -0
  135. package/src/prover/statetransition/StateTransitionProvable.ts +40 -0
  136. package/src/prover/statetransition/StateTransitionProver.ts +270 -0
  137. package/src/prover/statetransition/StateTransitionWitnessProvider.ts +24 -0
  138. package/src/prover/statetransition/StateTransitionWitnessProviderReference.ts +17 -0
  139. package/src/utils/PrefixedProvableHashList.ts +21 -0
  140. package/src/utils/ProvableHashList.ts +50 -0
  141. package/src/utils/merkletree/InMemoryMerkleTreeStorage.ts +99 -0
  142. package/src/utils/merkletree/MerkleTreeStore.ts +15 -0
  143. package/src/utils/merkletree/RollupMerkleTree.ts +250 -0
  144. package/src/utils/merkletree/VirtualMerkleTreeStore.ts +21 -0
  145. package/src/utils/utils.ts +103 -0
  146. package/test/BlockProver.test.ts +127 -0
  147. package/test/Protocol.test.ts +27 -0
  148. package/test/StateTransition.test.ts +182 -0
  149. package/tsconfig.json +8 -0
  150. package/tsconfig.test.json +9 -0
@@ -0,0 +1,250 @@
1
+ /* eslint-disable id-length */
2
+ /* eslint-disable line-comment-position */
3
+ /* eslint-disable no-inline-comments */
4
+ /* eslint-disable @typescript-eslint/no-magic-numbers */
5
+ import { Bool, Circuit, Field, Poseidon, Struct } from "snarkyjs";
6
+
7
+ import { notInCircuit } from "../utils";
8
+ import { MerkleTreeStore } from "./MerkleTreeStore";
9
+
10
+ // external API
11
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
12
+ export { RollupMerkleTree, RollupMerkleWitness };
13
+
14
+ // internal API
15
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
16
+ export { maybeSwap };
17
+
18
+ /**
19
+ * The {@link BaseMerkleWitness} class defines a circuit-compatible base class
20
+ * for [Merkle Witness'](https://computersciencewiki.org/index.php/Merkle_proof).
21
+ */
22
+ class RollupMerkleWitness extends Struct({
23
+ path: Circuit.array(Field, 256 - 1),
24
+ isLeft: Circuit.array(Bool, 256 - 1),
25
+ }) {
26
+ public static height = 256;
27
+
28
+ public height(): number {
29
+ return RollupMerkleWitness.height;
30
+ }
31
+
32
+ /**
33
+ * Calculates a root depending on the leaf value.
34
+ * @param leaf Value of the leaf node that belongs to this Witness.
35
+ * @returns The calculated root.
36
+ */
37
+ public calculateRoot(hash: Field): Field {
38
+ const n = this.height();
39
+
40
+ for (let index = 1; index < n; ++index) {
41
+ const isLeft = this.isLeft[index - 1];
42
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
43
+ const [left, right] = maybeSwap(isLeft, hash, this.path[index - 1]);
44
+ hash = Poseidon.hash([left, right]);
45
+ }
46
+
47
+ return hash;
48
+ }
49
+
50
+ /**
51
+ * Calculates the index of the leaf node that belongs to this Witness.
52
+ * @returns Index of the leaf.
53
+ */
54
+ public calculateIndex(): Field {
55
+ let powerOfTwo = Field(1);
56
+ let index = Field(0);
57
+ const n = this.height();
58
+
59
+ // eslint-disable-next-line no-underscore-dangle
60
+ for (let index_ = 1; index_ < n; ++index_) {
61
+ index = Circuit.if(this.isLeft[index_ - 1], index, index.add(powerOfTwo));
62
+ powerOfTwo = powerOfTwo.mul(2);
63
+ }
64
+
65
+ return index;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * A [Merkle Tree](https://en.wikipedia.org/wiki/Merkle_tree) is a binary tree in
71
+ * which every leaf is the cryptography hash of a piece of data,
72
+ * and every node is the hash of the concatenation of its two child nodes.
73
+ *
74
+ * A Merkle Tree allows developers to easily and securely verify
75
+ * the integrity of large amounts of data.
76
+ *
77
+ * Take a look at our [documentation](https://docs.minaprotocol.com/en/zkapps)
78
+ * on how to use Merkle Trees in combination with zkApps and
79
+ * zero knowledge programming!
80
+ *
81
+ * Levels are indexed from leaves (level 0) to root (level N - 1).
82
+ */
83
+ class RollupMerkleTree {
84
+ public static height = 256;
85
+
86
+ public static get leafCount(): bigint {
87
+ return 2n ** BigInt(RollupMerkleTree.height - 1);
88
+ }
89
+
90
+ private readonly zeroes: bigint[];
91
+
92
+ public readonly store: MerkleTreeStore;
93
+
94
+ public constructor(store: MerkleTreeStore) {
95
+ this.store = store;
96
+ // eslint-disable-next-line @shopify/prefer-class-properties
97
+ this.zeroes = [0n];
98
+ for (let index = 1; index < RollupMerkleTree.height; index += 1) {
99
+ const previousLevel = Field(this.zeroes[index - 1]);
100
+ this.zeroes.push(
101
+ Poseidon.hash([previousLevel, previousLevel]).toBigInt()
102
+ );
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Returns a node which lives at a given index and level.
108
+ * @param level Level of the node.
109
+ * @param index Index of the node.
110
+ * @returns The data of the node.
111
+ */
112
+ @notInCircuit()
113
+ public getNode(level: number, index: bigint): Field {
114
+ return Field(this.store.getNode(index, level) ?? this.zeroes[level]);
115
+ }
116
+
117
+ /**
118
+ * Returns the root of the [Merkle Tree](https://en.wikipedia.org/wiki/Merkle_tree).
119
+ * @returns The root of the Merkle Tree.
120
+ */
121
+ @notInCircuit()
122
+ public getRoot(): Field {
123
+ return this.getNode(RollupMerkleTree.height - 1, 0n);
124
+ }
125
+
126
+ // eslint-disable-next-line no-warning-comments
127
+ // TODO: this allows to set a node at an index larger than the size. OK?
128
+ private setNode(level: number, index: bigint, value: Field) {
129
+ this.store.setNode(index, level, value.toBigInt());
130
+ }
131
+
132
+ /**
133
+ * TODO: if this is passed an index bigger than the max, it will set a couple
134
+ * of out-of-bounds nodes but not affect the real Merkle root. OK?
135
+ */
136
+
137
+ /**
138
+ * Sets the value of a leaf node at a given index to a given value.
139
+ * @param index Position of the leaf node.
140
+ * @param leaf New value.
141
+ */
142
+ @notInCircuit()
143
+ public setLeaf(index: bigint, leaf: Field) {
144
+ if (index >= this.leafCount) {
145
+ index %= this.leafCount;
146
+ }
147
+ this.setNode(0, index, leaf);
148
+ let currentIndex = index;
149
+ for (let level = 1; level < RollupMerkleTree.height; level += 1) {
150
+ currentIndex /= 2n;
151
+
152
+ const left = this.getNode(level - 1, currentIndex * 2n);
153
+ const right = this.getNode(level - 1, currentIndex * 2n + 1n);
154
+
155
+ this.setNode(level, currentIndex, Poseidon.hash([left, right]));
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Returns the witness (also known as
161
+ * [Merkle Proof or Merkle Witness](https://computersciencewiki.org/index.php/Merkle_proof))
162
+ * for the leaf at the given index.
163
+ * @param index Position of the leaf node.
164
+ * @returns The witness that belongs to the leaf.
165
+ */
166
+ @notInCircuit()
167
+ public getWitness(index: bigint): RollupMerkleWitness {
168
+ if (index >= this.leafCount) {
169
+ index %= this.leafCount;
170
+ }
171
+ const path = [];
172
+ const isLefts = [];
173
+ for (let level = 0; level < RollupMerkleTree.height - 1; level += 1) {
174
+ const isLeft = index % 2n === 0n;
175
+ const sibling = this.getNode(level, isLeft ? index + 1n : index - 1n);
176
+ isLefts.push(Bool(isLeft));
177
+ path.push(sibling);
178
+ index /= 2n;
179
+ }
180
+ return new RollupMerkleWitness({
181
+ isLeft: isLefts,
182
+ path,
183
+ });
184
+ }
185
+
186
+ // eslint-disable-next-line no-warning-comments, max-len
187
+ // TODO: should this take an optional offset? should it fail if the array is too long?
188
+ /**
189
+ * Fills all leaves of the tree.
190
+ * @param leaves Values to fill the leaves with.
191
+ */
192
+ @notInCircuit()
193
+ public fill(leaves: Field[]) {
194
+ leaves.forEach((value, index) => {
195
+ this.setLeaf(BigInt(index), value);
196
+ });
197
+ }
198
+
199
+ /**
200
+ * Returns the amount of leaf nodes.
201
+ * @returns Amount of leaf nodes.
202
+ */
203
+ public get leafCount(): bigint {
204
+ return RollupMerkleTree.leafCount;
205
+ }
206
+ }
207
+
208
+ // eslint-disable-next-line @typescript-eslint/no-namespace
209
+ export namespace MerkleTreeUtils {
210
+ export function normalizeKey(key: Field): Field {
211
+ // if(NJORD_MERKLE_TREE_HEIGHT < 256){
212
+ // return fieldMod(key, Field(RollupMerkleTree.leafCount).toConstant())
213
+ // eslint-disable-next-line max-len
214
+ // // return modPower2(key, RollupMerkleTree.height - 1) //TODO Fix modPower2
215
+ // }else{
216
+ return key;
217
+
218
+ // }
219
+ }
220
+
221
+ export function checkMembership(
222
+ witness: RollupMerkleWitness,
223
+ root: Field,
224
+ key: Field,
225
+ value: Field
226
+ ): Bool {
227
+ const root2 = witness.calculateRoot(value);
228
+ const key2 = witness.calculateIndex();
229
+ key.assertEquals(key2, "Keys of MerkleWitness does not match");
230
+ return root.equals(root2);
231
+ }
232
+
233
+ export function computeRoot(
234
+ witness: RollupMerkleWitness,
235
+ value: Field
236
+ ): Field {
237
+ return witness.calculateRoot(value);
238
+ }
239
+ }
240
+
241
+ /**
242
+ * More efficient version of `maybeSwapBad` which
243
+ * reuses an intermediate variable
244
+ */
245
+ function maybeSwap(b: Bool, x: Field, y: Field): [Field, Field] {
246
+ const m = b.toField().mul(x.sub(y)); // b*(x - y)
247
+ const x1 = y.add(m); // y + b*(x - y)
248
+ const y2 = x.sub(m); // x - b*(x - y) = x + b*(y - x)
249
+ return [x1, y2];
250
+ }
@@ -0,0 +1,21 @@
1
+ import { MerkleTreeStore } from "./MerkleTreeStore";
2
+ import { InMemoryMerkleTreeStorage } from "./InMemoryMerkleTreeStorage";
3
+
4
+ /**
5
+ * A MemoryMerkleTreeStore that, if falls back to a parent store if it
6
+ * has no data
7
+ */
8
+ export class VirtualMerkleTreeStore extends InMemoryMerkleTreeStorage {
9
+ public constructor(private readonly parent: MerkleTreeStore) {
10
+ super()
11
+ }
12
+
13
+ public getNode(key: bigint, level: number): bigint | undefined {
14
+ return super.getNode(key, level) ?? this.parent.getNode(key, level);
15
+ }
16
+
17
+ public setNode(key: bigint, level: number, value: bigint): void {
18
+ super.setNode(key, level, value);
19
+ }
20
+
21
+ }
@@ -0,0 +1,103 @@
1
+ // eslint-disable-next-line max-len
2
+ /* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/ban-types, @typescript-eslint/no-unsafe-return,@typescript-eslint/no-empty-function */
3
+
4
+ import { TextEncoder, TextDecoder } from "node:util";
5
+
6
+ import { Circuit, Field, Poseidon, Proof } from "snarkyjs";
7
+
8
+ export type ReturnType<FunctionType extends Function> = FunctionType extends (
9
+ ...args: any[]
10
+ ) => infer Return
11
+ ? Return
12
+ : any;
13
+
14
+ export type UnTypedClass = new (...args: any[]) => any;
15
+
16
+ export type TypedClass<Class> = new (...args: any[]) => Class;
17
+
18
+ export type Subclass<Class extends new (...args: any) => any> = (new (
19
+ ...args: any
20
+ ) => InstanceType<Class>) & {
21
+ [Key in keyof Class]: Class[Key];
22
+ } & { prototype: InstanceType<Class> };
23
+
24
+ export function notInCircuit(): MethodDecorator {
25
+ return function ReplacedFunction(
26
+ target: any,
27
+ propertyKey: string | symbol,
28
+ descriptor: PropertyDescriptor
29
+ ) {
30
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
31
+ const childFunction = descriptor.value;
32
+ descriptor.value = function value(this: any, ...args: any[]) {
33
+ if (Circuit.inCheckedComputation() || Circuit.inProver()) {
34
+ throw new Error(
35
+ `Method ${propertyKey.toString()} is supposed to be only called outside of the circuit`
36
+ );
37
+ }
38
+ // eslint-disable-next-line max-len
39
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
40
+ return childFunction.apply(this, args);
41
+ };
42
+ return descriptor;
43
+ };
44
+ }
45
+
46
+ export function stringToField(value: string, throwOnOverflow = false) {
47
+ const fieldSize = Field.sizeInBytes();
48
+
49
+ const encoder = new TextEncoder();
50
+
51
+ const stringBytes = Array.from(encoder.encode(value));
52
+
53
+ const padding = Array.from<number>({
54
+ length: fieldSize - stringBytes.length,
55
+ }).fill(0);
56
+ const data = stringBytes.concat(padding);
57
+
58
+ if (data.length > fieldSize) {
59
+ if (throwOnOverflow) {
60
+ throw new Error(
61
+ "Trying to encode a stringt that is larger than 256 bits"
62
+ );
63
+ }
64
+
65
+ // Hash the result Field[] to reduce it to
66
+ const chunks = data.reduce<number[][]>((a, b, index) => {
67
+ const arrayIndex = index / fieldSize;
68
+ if (a.length <= arrayIndex) {
69
+ a.push([]);
70
+ }
71
+ a[arrayIndex].push(b);
72
+ return a;
73
+ }, []);
74
+ return Poseidon.hash(chunks.map((x) => Field.fromBytes(x)));
75
+ }
76
+
77
+ return Field.fromBytes(data);
78
+ }
79
+
80
+ /**
81
+ * Note: This only works for strings that have been encoded using the `throwOnOverflow` set to true
82
+ */
83
+ export function fieldToString(value: Field | bigint): string {
84
+ if (typeof value === "bigint") {
85
+ value = Field(value);
86
+ }
87
+ let bytes = Field.toBytes(value);
88
+ // Find start of padded zeroes in order to remove them.
89
+ const zeroesStart =
90
+ bytes.length -
91
+ bytes
92
+ .slice()
93
+ .reverse()
94
+ .findIndex((element) => element !== 0);
95
+
96
+ bytes = bytes.slice(0, zeroesStart);
97
+
98
+ const decoder = new TextDecoder();
99
+
100
+ return decoder.decode(new Uint8Array(bytes));
101
+ }
102
+
103
+ export function noop(): void {}
@@ -0,0 +1,127 @@
1
+ import "reflect-metadata";
2
+ import { Bool, Field, Poseidon, Proof } from "snarkyjs";
3
+ import {
4
+ container as globalContainer,
5
+ type DependencyContainer,
6
+ } from "tsyringe";
7
+
8
+ import { MethodPublicOutput } from "@proto-kit/protocol";
9
+
10
+ import {
11
+ BlockProver,
12
+ type BlockProverState,
13
+ } from "../src/prover/block/BlockProver.js";
14
+ import { NoOpStateTransitionWitnessProvider } from "../src/prover/statetransition/StateTransitionWitnessProvider.js";
15
+ import {
16
+ StateTransitionProverPublicInput,
17
+ StateTransitionProverPublicOutput,
18
+ } from "../src/prover/statetransition/StateTransitionProvable";
19
+ import { BlockProverPublicInput } from "../src/prover/block/BlockProvable";
20
+
21
+ type BlockProverProofPair = [
22
+ Proof<void, MethodPublicOutput>,
23
+ Proof<StateTransitionProverPublicInput, StateTransitionProverPublicOutput>
24
+ ];
25
+
26
+ describe("blockProver", () => {
27
+ let container: DependencyContainer;
28
+
29
+ beforeEach(() => {
30
+ const childContainer = globalContainer.createChildContainer();
31
+ childContainer.register(
32
+ "StateTransitionWitnessProvider",
33
+ NoOpStateTransitionWitnessProvider
34
+ );
35
+ container = childContainer;
36
+ });
37
+
38
+ function generateTestProofs(
39
+ fromStateRoot: Field,
40
+ toStateRoot: Field
41
+ ): BlockProverProofPair {
42
+ const transactionHash = Poseidon.hash([Field(12_345)]);
43
+ const sthash = Field(123);
44
+
45
+ const appProof = new Proof<undefined, MethodPublicOutput>({
46
+ publicInput: undefined,
47
+ publicOutput: new MethodPublicOutput({
48
+ transactionHash,
49
+ stateTransitionsHash: sthash,
50
+ status: Bool(true),
51
+ }),
52
+
53
+ proof: "",
54
+ maxProofsVerified: 2,
55
+ });
56
+
57
+ const stProof = new Proof<
58
+ StateTransitionProverPublicInput,
59
+ StateTransitionProverPublicOutput
60
+ >({
61
+ publicInput: new StateTransitionProverPublicInput({
62
+ stateTransitionsHash: Field(0),
63
+ stateRoot: fromStateRoot,
64
+ }),
65
+ publicOutput: new StateTransitionProverPublicOutput({
66
+ stateTransitionsHash: sthash,
67
+ stateRoot: toStateRoot,
68
+ }),
69
+
70
+ proof: "",
71
+ maxProofsVerified: 2,
72
+ });
73
+
74
+ return [appProof, stProof];
75
+ }
76
+
77
+ it("should pass with valid inputs", () => {
78
+ expect.assertions(0);
79
+
80
+ const blockProver = container.resolve(BlockProver);
81
+
82
+ const fromState = Field(1);
83
+ const toState = Field(2);
84
+
85
+ const [appProof, stProof] = generateTestProofs(fromState, toState);
86
+
87
+ const state: BlockProverState = {
88
+ stateRoot: fromState,
89
+ transactionsHash: Field(0),
90
+ };
91
+ blockProver.applyTransaction(state, stProof, appProof);
92
+ });
93
+
94
+ it("previously applied transaction should also pass with derived publicInputs", () => {
95
+ expect.assertions(2);
96
+
97
+ const blockProver = container.resolve(BlockProver);
98
+
99
+ const fromState = Field(1);
100
+ const toState = Field(2);
101
+
102
+ const [appProof, stProof] = generateTestProofs(fromState, toState);
103
+
104
+ const fromProverState: BlockProverState = {
105
+ stateRoot: fromState,
106
+ transactionsHash: Field(0),
107
+ };
108
+ const toProverState = { ...fromProverState };
109
+ blockProver.applyTransaction(toProverState, stProof, appProof);
110
+
111
+ const publicInput = new BlockProverPublicInput({
112
+ stateRoot: fromProverState.stateRoot,
113
+ transactionsHash: fromProverState.transactionsHash,
114
+ });
115
+
116
+ const publicOutput = blockProver.proveTransaction(
117
+ publicInput,
118
+ stProof,
119
+ appProof
120
+ );
121
+
122
+ expect(publicOutput.stateRoot).toStrictEqual(toProverState.stateRoot);
123
+ expect(publicOutput.transactionsHash).toStrictEqual(
124
+ toProverState.transactionsHash
125
+ );
126
+ });
127
+ });
@@ -0,0 +1,27 @@
1
+ import "reflect-metadata";
2
+ import { VanillaProtocol } from "../src/protocol/Protocol";
3
+ import { beforeEach } from "@jest/globals";
4
+ import { BlockProver } from "../src/prover/block/BlockProver";
5
+ import { StateTransitionProver } from "../src/prover/statetransition/StateTransitionProver";
6
+ import { NoOpStateTransitionWitnessProvider } from "../src";
7
+
8
+ describe("protocol", () => {
9
+ let protocol: ReturnType<typeof VanillaProtocol.create>;
10
+
11
+ beforeEach(() => {
12
+ protocol = VanillaProtocol.create();
13
+ });
14
+
15
+ it("should resolve all provers correctly", async () => {
16
+ expect.assertions(2);
17
+
18
+ protocol.dependencyContainer.register("StateTransitionWitnessProvider", {
19
+ useValue: new NoOpStateTransitionWitnessProvider()
20
+ })
21
+
22
+ expect(protocol.blockProver instanceof BlockProver).toBe(true);
23
+ expect(
24
+ protocol.stateTransitionProver instanceof StateTransitionProver
25
+ ).toBe(true);
26
+ });
27
+ });
@@ -0,0 +1,182 @@
1
+ import "reflect-metadata";
2
+ import { Bool, Field } from "snarkyjs";
3
+ import { container } from "tsyringe";
4
+
5
+ import {
6
+ Option,
7
+ ProvableStateTransition,
8
+ DefaultProvableHashList,
9
+ StateTransitionProvableBatch, CachedMerkleTreeStore
10
+ } from "../src/index";
11
+ import {
12
+ RollupMerkleTree,
13
+ type RollupMerkleWitness,
14
+ } from "../src/utils/merkletree/RollupMerkleTree.js";
15
+ import { StateTransitionProver } from "../src/prover/statetransition/StateTransitionProver.js";
16
+ import type { StateTransitionWitnessProvider } from "../src/prover/statetransition/StateTransitionWitnessProvider.js";
17
+
18
+ describe("stateTransition", () => {
19
+ async function checkTransitions(
20
+ tree: RollupMerkleTree,
21
+ transitions: ProvableStateTransition[]
22
+ ) {
23
+ const batch = StateTransitionProvableBatch.fromTransitions(transitions);
24
+
25
+ const temporaryTree = new RollupMerkleTree(
26
+ new CachedMerkleTreeStore(tree.store)
27
+ );
28
+ const startRoot = temporaryTree.getRoot();
29
+
30
+ const hashList = new DefaultProvableHashList(ProvableStateTransition);
31
+
32
+ batch.batch.forEach((item) => {
33
+ if (item.to.isSome.toBoolean()) {
34
+ temporaryTree.setLeaf(item.path.toBigInt(), item.to.value);
35
+ }
36
+ hashList.push(item);
37
+ });
38
+
39
+ const endRoot = temporaryTree.getRoot();
40
+
41
+ class DummySTWP implements StateTransitionWitnessProvider {
42
+ private i = 0;
43
+
44
+ public constructor(private readonly witnessTree: RollupMerkleTree) {}
45
+
46
+ public getWitness(key: Field): RollupMerkleWitness {
47
+ const witness = this.witnessTree.getWitness(key.toBigInt());
48
+ const set = batch.batch[this.i];
49
+ if (set.to.isSome.toBoolean()) {
50
+ this.witnessTree.setLeaf(key.toBigInt(), set.to.value);
51
+ }
52
+ this.i += 1;
53
+ return witness;
54
+ }
55
+ }
56
+
57
+ const childContainer = container.createChildContainer();
58
+ childContainer.registerInstance(
59
+ "StateTransitionWitnessProvider",
60
+ new DummySTWP(tree)
61
+ );
62
+ const prover = childContainer.resolve(StateTransitionProver);
63
+
64
+ const state = prover.applyTransitions(startRoot, Field(0), batch);
65
+
66
+ expect(state.stateRoot).toStrictEqual(endRoot);
67
+ expect(state.stateTransitionList.commitment).toStrictEqual(
68
+ hashList.commitment
69
+ );
70
+
71
+ await childContainer.dispose();
72
+ }
73
+
74
+ it.each([
75
+ [
76
+ [
77
+ new ProvableStateTransition({
78
+ from: Option.fromValue(Field(1), Field).toProvable(),
79
+ to: Option.fromValue(Field(14), Field).toProvable(),
80
+ path: Field(1),
81
+ }),
82
+ new ProvableStateTransition({
83
+ from: Option.fromValue(Field(14), Field).toProvable(),
84
+ to: Option.fromValue(Field(4), Field).toProvable(),
85
+ path: Field(1),
86
+ }),
87
+ ],
88
+ ],
89
+ [
90
+ [
91
+ new ProvableStateTransition({
92
+ from: Option.none().toProvable(),
93
+ to: Option.from(Bool(true), Field(4), Field).toProvable(),
94
+ path: Field(1),
95
+ }),
96
+ new ProvableStateTransition({
97
+ from: Option.from(Bool(true), Field(5), Field).toProvable(),
98
+ to: Option.from(Bool(true), Field(2), Field).toProvable(),
99
+ path: Field(2),
100
+ }),
101
+ new ProvableStateTransition({
102
+ from: Option.from(Bool(true), Field(2), Field).toProvable(),
103
+ to: Option.none().toProvable(),
104
+ path: Field(2),
105
+ }),
106
+ ],
107
+ ],
108
+ ])("should pass without throwing", async (transitions) => {
109
+ expect.assertions(2);
110
+
111
+ const tree = new RollupMerkleTree(new MemoryMerkleTreeStorage());
112
+
113
+ // Is ignored because overwritten by first transition
114
+ tree.setLeaf(1n, Option.fromValue(Field(1), Field).treeValue);
115
+ tree.setLeaf(2n, Option.fromValue(Field(5), Field).treeValue);
116
+
117
+ await checkTransitions(tree, transitions);
118
+ });
119
+
120
+ it.each([
121
+ [
122
+ [
123
+ new ProvableStateTransition({
124
+ // fail
125
+ from: Option.from(Bool(true), Field(2), Field).toProvable(),
126
+ to: Option.none().toProvable(),
127
+ path: Field(1),
128
+ }),
129
+ ],
130
+ 0,
131
+ ],
132
+ [
133
+ [
134
+ new ProvableStateTransition({
135
+ // success
136
+ from: Option.from(Bool(true), Field(1), Field).toProvable(),
137
+ to: Option.from(Bool(true), Field(14), Field).toProvable(),
138
+ path: Field(1),
139
+ }),
140
+ new ProvableStateTransition({
141
+ // fail
142
+ from: Option.from(Bool(true), Field(6), Field).toProvable(),
143
+ to: Option.none().toProvable(),
144
+ path: Field(2),
145
+ }),
146
+ ],
147
+ 1,
148
+ ],
149
+ [
150
+ [
151
+ new ProvableStateTransition({
152
+ // success
153
+ from: Option.from(Bool(true), Field(1), Field).toProvable(),
154
+ to: Option.from(Bool(true), Field(14), Field).toProvable(),
155
+ path: Field(1),
156
+ }),
157
+ new ProvableStateTransition({
158
+ // fail
159
+ from: Option.from(Bool(true), Field(15), Field).toProvable(),
160
+ to: Option.none().toProvable(),
161
+ path: Field(1),
162
+ }),
163
+ ],
164
+ 1,
165
+ ],
166
+ ])(
167
+ "should throw because of failing precondition",
168
+ async (transitions, index) => {
169
+ expect.assertions(1);
170
+
171
+ const tree = new RollupMerkleTree(new MemoryMerkleTreeStorage());
172
+
173
+ // Is ignored because overwritten by first transition
174
+ tree.setLeaf(1n, Option.fromValue(Field(1), Field).treeValue);
175
+ tree.setLeaf(2n, Option.fromValue(Field(5), Field).treeValue);
176
+
177
+ await expect(checkTransitions(tree, transitions)).rejects.toThrow(
178
+ `MerkleWitness not valid for StateTransition (${index})`
179
+ );
180
+ }
181
+ );
182
+ });