From 1083d82a8b0f77e4d02b5eb231a5fdf9cd7c6cbf Mon Sep 17 00:00:00 2001 From: ymekuria Date: Tue, 16 Jul 2024 15:06:58 -0700 Subject: [PATCH 01/49] chore(bindings): update subproject commit reference to 7705ca8e99bbe03bb702490d7aac179456cc19b4 --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 5bf4a92ab7..7705ca8e99 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 5bf4a92ab7467412f9953bbc29d97bfd19230db0 +Subproject commit 7705ca8e99bbe03bb702490d7aac179456cc19b4 From 810bf31bf67cc5f3de10625a2a59dbd428d23e5c Mon Sep 17 00:00:00 2001 From: ymekuria Date: Fri, 26 Jul 2024 15:04:59 -0600 Subject: [PATCH 02/49] chore(bindings): update subproject commit reference to a98e6cfa0fd91bd529ca0b3b461144ef7e5c1284 --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 7705ca8e99..a98e6cfa0f 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 7705ca8e99bbe03bb702490d7aac179456cc19b4 +Subproject commit a98e6cfa0fd91bd529ca0b3b461144ef7e5c1284 From a107b42a7f239d73cdd745749d6f2a5622fbde61 Mon Sep 17 00:00:00 2001 From: ymekuria Date: Sun, 28 Jul 2024 23:42:39 -0600 Subject: [PATCH 03/49] chore(bindings): update subproject commit reference to 1f9522301 for consistency --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index a98e6cfa0f..1f9522301a 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit a98e6cfa0fd91bd529ca0b3b461144ef7e5c1284 +Subproject commit 1f9522301a8f6ec795e2214a3b4f9107005e2910 From 5736917e6661f56a68c698b8c45efeb6edaea8be Mon Sep 17 00:00:00 2001 From: ymekuria Date: Sun, 28 Jul 2024 23:51:20 -0600 Subject: [PATCH 04/49] chore(mina): update subproject commit reference to fe1761756 for consistency --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index b6b427ab9c..fe17617563 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit b6b427ab9c2c30f76e4dbaa51925f8eb1a66552f +Subproject commit fe17617563897f041a73530ad98a118b37eec665 From de8f46ba71bbc9af4ae1cf5991eff755aedbe523 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Mon, 29 Jul 2024 09:07:30 +0300 Subject: [PATCH 05/49] Validation. --- src/lib/provable/foreign-field.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts index 4f6a736c5a..e6042600d8 100644 --- a/src/lib/provable/foreign-field.ts +++ b/src/lib/provable/foreign-field.ts @@ -14,6 +14,7 @@ import { ForeignField as FF } from './gadgets/foreign-field.js'; import { assert } from './gadgets/common.js'; import { l3, l } from './gadgets/range-check.js'; import { ProvablePureExtended } from './types/struct.js'; +import { isField } from './core/field-constructor.js'; // external API export { createForeignField }; @@ -96,12 +97,28 @@ class ForeignField { constructor(x: ForeignField | Field3 | bigint | number | string) { const p = this.modulus; if (x instanceof ForeignField) { - this.value = x.value; + if (x.modulus !== p) { + throw new Error( + `ForeignField constructor: modulus mismatch. Expected ${p}, got ${x.modulus}. Please use ForeignField.reduce() or provide a value with the correct modulus.` + ); + } + if (x.isConstant()) { + this.value = Field3.from(mod(x.toBigInt(), p)); + } else { + this.value = x.value; + this.assertCanonical(); + } return; } // Field3 if (Array.isArray(x)) { + if (x.some((limb) => !isField(limb))) { + throw new Error( + `ForeignField constructor: invalid Field3 element. Please provide valid Field elements.` + ); + } this.value = x; + this.assertCanonical(); return; } // constant From d9a6abf6926d251faa8bf770e3c17c95490bbcdf Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Mon, 29 Jul 2024 17:25:22 +0300 Subject: [PATCH 06/49] Refactoring. --- src/lib/provable/foreign-field.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts index e6042600d8..1246ed63b7 100644 --- a/src/lib/provable/foreign-field.ts +++ b/src/lib/provable/foreign-field.ts @@ -118,7 +118,7 @@ class ForeignField { ); } this.value = x; - this.assertCanonical(); + this.assertLessThan(this.modulus); return; } // constant From f9f317ee05243451751b408ae42d5c040ba5ffe3 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Mon, 29 Jul 2024 19:06:36 +0300 Subject: [PATCH 07/49] Revert changes and comment. --- src/lib/provable/foreign-field.ts | 40 +++++++++++++++++-------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts index 1246ed63b7..6dcabd74e7 100644 --- a/src/lib/provable/foreign-field.ts +++ b/src/lib/provable/foreign-field.ts @@ -14,7 +14,6 @@ import { ForeignField as FF } from './gadgets/foreign-field.js'; import { assert } from './gadgets/common.js'; import { l3, l } from './gadgets/range-check.js'; import { ProvablePureExtended } from './types/struct.js'; -import { isField } from './core/field-constructor.js'; // external API export { createForeignField }; @@ -93,32 +92,37 @@ class ForeignField { * ```ts * let x = new ForeignField(5); * ``` + * _Note:_ Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. + * + * - When constructing from another {@link ForeignField} instance, ensure the modulus matches. If not, reduce the value first. + * @example + * ```ts + * let smallField = new SmallField(0); + * let largerField = new LargerField(17); + * let reducedField = SmallField.reduce(largerField); + * let x = new SmallField(reducedField); + * ``` + * - When constructing from a {@link Field3} array, ensure all elements are valid Field elements and range checked. + * @example + * ```ts + * let field3Array = [Field(15), Field(0), Field(0)]; + * let x = new ForeignField(field3Array); + * ``` + * - Ensure constants are correctly reduced to the modulus of the field. + * @example + * ```ts + * let x = new ForeignField(20); // Automatically reduced to the field's modulus + * ``` */ constructor(x: ForeignField | Field3 | bigint | number | string) { const p = this.modulus; if (x instanceof ForeignField) { - if (x.modulus !== p) { - throw new Error( - `ForeignField constructor: modulus mismatch. Expected ${p}, got ${x.modulus}. Please use ForeignField.reduce() or provide a value with the correct modulus.` - ); - } - if (x.isConstant()) { - this.value = Field3.from(mod(x.toBigInt(), p)); - } else { - this.value = x.value; - this.assertCanonical(); - } + this.value = x.value; return; } // Field3 if (Array.isArray(x)) { - if (x.some((limb) => !isField(limb))) { - throw new Error( - `ForeignField constructor: invalid Field3 element. Please provide valid Field elements.` - ); - } this.value = x; - this.assertLessThan(this.modulus); return; } // constant From 816e5f22944ee5ea8641aef5230b7c50f7c55b70 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Tue, 30 Jul 2024 16:11:20 +0300 Subject: [PATCH 08/49] Update foreign-field constructor comment. --- src/lib/provable/foreign-field.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts index 6dcabd74e7..2ee9f952e5 100644 --- a/src/lib/provable/foreign-field.ts +++ b/src/lib/provable/foreign-field.ts @@ -94,13 +94,13 @@ class ForeignField { * ``` * _Note:_ Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. * - * - When constructing from another {@link ForeignField} instance, ensure the modulus matches. If not, reduce the value first. + * - When constructing from another {@link ForeignField} instance, ensure the modulus matches. If not, check the modulus using `Gadgets.ForeignField.assertLessThan()` and handle appropriately. * @example * ```ts * let smallField = new SmallField(0); * let largerField = new LargerField(17); - * let reducedField = SmallField.reduce(largerField); - * let x = new SmallField(reducedField); + * Gadgets.ForeignField.assertLessThan(largerField.value, SmallField.modulus); + * let x = new SmallField(largerField); * ``` * - When constructing from a {@link Field3} array, ensure all elements are valid Field elements and range checked. * @example From 9c87a7e02acc809207a6cd5e74d97df1e10a6222 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Tue, 30 Jul 2024 16:59:04 +0300 Subject: [PATCH 09/49] Addressing review comments. --- src/lib/provable/foreign-field.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts index 2ee9f952e5..4fb580887a 100644 --- a/src/lib/provable/foreign-field.ts +++ b/src/lib/provable/foreign-field.ts @@ -97,7 +97,10 @@ class ForeignField { * - When constructing from another {@link ForeignField} instance, ensure the modulus matches. If not, check the modulus using `Gadgets.ForeignField.assertLessThan()` and handle appropriately. * @example * ```ts - * let smallField = new SmallField(0); + * class SmallField extends createForeignField(17n) {} + * class LargerField extends createForeignField(19n) {} + * + * let smallField = new SmallField(10); * let largerField = new LargerField(17); * Gadgets.ForeignField.assertLessThan(largerField.value, SmallField.modulus); * let x = new SmallField(largerField); From afa75584ef99ad3874360bee294912db7cf2db3f Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Tue, 30 Jul 2024 19:02:42 +0300 Subject: [PATCH 10/49] Addressing review comments. --- src/lib/provable/crypto/foreign-curve.ts | 3 +++ src/lib/provable/crypto/foreign-ecdsa.ts | 4 +++- src/lib/provable/foreign-field.ts | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lib/provable/crypto/foreign-curve.ts b/src/lib/provable/crypto/foreign-curve.ts index ef8d7eb136..47c91e217f 100644 --- a/src/lib/provable/crypto/foreign-curve.ts +++ b/src/lib/provable/crypto/foreign-curve.ts @@ -12,6 +12,7 @@ import { assert } from '../gadgets/common.js'; import { Provable } from '../provable.js'; import { provableFromClass } from '../types/provable-derivers.js'; import { l2Mask, multiRangeCheck } from '../gadgets/range-check.js'; +import type { ForeignField } from '../foreign-field.js'; // external API export { @@ -42,6 +43,8 @@ class ForeignCurve { /** * Create a new {@link ForeignCurve} from an object representing the (affine) x and y coordinates. * + * _Note:_ Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. Refer to {@link ForeignField} constructor comments for more details. + * * @example * ```ts * let x = new ForeignCurve({ x: 1n, y: 1n }); diff --git a/src/lib/provable/crypto/foreign-ecdsa.ts b/src/lib/provable/crypto/foreign-ecdsa.ts index 300da146c2..079e7020db 100644 --- a/src/lib/provable/crypto/foreign-ecdsa.ts +++ b/src/lib/provable/crypto/foreign-ecdsa.ts @@ -18,6 +18,7 @@ import { Keccak } from './keccak.js'; import { Bytes } from '../wrapped-classes.js'; import { UInt8 } from '../int.js'; import type { Bool } from '../bool.js'; +import type { ForeignField } from '../foreign-field.js'; // external API export { createEcdsa, createEcdsaV2, EcdsaSignature, EcdsaSignatureV2 }; @@ -38,7 +39,8 @@ class EcdsaSignature { /** * Create a new {@link EcdsaSignature} from an object containing the scalars r and s. - * @param signature + * + * _Note:_ Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. Refer to {@link ForeignField} constructor comments for more details. */ constructor(signature: { r: AlmostForeignField | Field3 | bigint | number; diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts index 4fb580887a..485453b583 100644 --- a/src/lib/provable/foreign-field.ts +++ b/src/lib/provable/foreign-field.ts @@ -92,6 +92,7 @@ class ForeignField { * ```ts * let x = new ForeignField(5); * ``` + * * _Note:_ Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. * * - When constructing from another {@link ForeignField} instance, ensure the modulus matches. If not, check the modulus using `Gadgets.ForeignField.assertLessThan()` and handle appropriately. From 0cbecd73e2b3826d67221b6a1ab3a5fef5aa489d Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Tue, 30 Jul 2024 20:03:58 +0300 Subject: [PATCH 11/49] Update src/lib/provable/crypto/foreign-curve.ts Co-authored-by: Yoni Mekuria --- src/lib/provable/crypto/foreign-curve.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/provable/crypto/foreign-curve.ts b/src/lib/provable/crypto/foreign-curve.ts index 47c91e217f..41b3296dfc 100644 --- a/src/lib/provable/crypto/foreign-curve.ts +++ b/src/lib/provable/crypto/foreign-curve.ts @@ -43,7 +43,7 @@ class ForeignCurve { /** * Create a new {@link ForeignCurve} from an object representing the (affine) x and y coordinates. * - * _Note:_ Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. Refer to {@link ForeignField} constructor comments for more details. + * Note: Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. Refer to {@link ForeignField} constructor comments for more details. * * @example * ```ts From 22b15ea848421ecef42e180d3bca4aae540705fa Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Tue, 30 Jul 2024 20:04:06 +0300 Subject: [PATCH 12/49] Update src/lib/provable/crypto/foreign-ecdsa.ts Co-authored-by: Yoni Mekuria --- src/lib/provable/crypto/foreign-ecdsa.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/provable/crypto/foreign-ecdsa.ts b/src/lib/provable/crypto/foreign-ecdsa.ts index 079e7020db..d8ecce4c3f 100644 --- a/src/lib/provable/crypto/foreign-ecdsa.ts +++ b/src/lib/provable/crypto/foreign-ecdsa.ts @@ -40,7 +40,7 @@ class EcdsaSignature { /** * Create a new {@link EcdsaSignature} from an object containing the scalars r and s. * - * _Note:_ Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. Refer to {@link ForeignField} constructor comments for more details. + *Note: Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. Refer to {@link ForeignField} constructor comments for more details. */ constructor(signature: { r: AlmostForeignField | Field3 | bigint | number; From 807c9b358b735002c185cfc4d53a80b6dfb3d0bc Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Tue, 30 Jul 2024 20:04:31 +0300 Subject: [PATCH 13/49] Update src/lib/provable/foreign-field.ts Co-authored-by: Yoni Mekuria --- src/lib/provable/foreign-field.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts index 485453b583..8c01366bce 100644 --- a/src/lib/provable/foreign-field.ts +++ b/src/lib/provable/foreign-field.ts @@ -93,7 +93,7 @@ class ForeignField { * let x = new ForeignField(5); * ``` * - * _Note:_ Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. + * Note: Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. * * - When constructing from another {@link ForeignField} instance, ensure the modulus matches. If not, check the modulus using `Gadgets.ForeignField.assertLessThan()` and handle appropriately. * @example From 84e4d5a274cd436448dab91581afa088ab0705d0 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Tue, 30 Jul 2024 21:02:19 +0300 Subject: [PATCH 14/49] Remove type importing for JSDoc links. --- src/lib/provable/crypto/foreign-curve.ts | 1 - src/lib/provable/crypto/foreign-ecdsa.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/lib/provable/crypto/foreign-curve.ts b/src/lib/provable/crypto/foreign-curve.ts index 41b3296dfc..01067b1c59 100644 --- a/src/lib/provable/crypto/foreign-curve.ts +++ b/src/lib/provable/crypto/foreign-curve.ts @@ -12,7 +12,6 @@ import { assert } from '../gadgets/common.js'; import { Provable } from '../provable.js'; import { provableFromClass } from '../types/provable-derivers.js'; import { l2Mask, multiRangeCheck } from '../gadgets/range-check.js'; -import type { ForeignField } from '../foreign-field.js'; // external API export { diff --git a/src/lib/provable/crypto/foreign-ecdsa.ts b/src/lib/provable/crypto/foreign-ecdsa.ts index d8ecce4c3f..5cbd03427d 100644 --- a/src/lib/provable/crypto/foreign-ecdsa.ts +++ b/src/lib/provable/crypto/foreign-ecdsa.ts @@ -18,7 +18,6 @@ import { Keccak } from './keccak.js'; import { Bytes } from '../wrapped-classes.js'; import { UInt8 } from '../int.js'; import type { Bool } from '../bool.js'; -import type { ForeignField } from '../foreign-field.js'; // external API export { createEcdsa, createEcdsaV2, EcdsaSignature, EcdsaSignatureV2 }; From 75f148b1edf03ded466b8057636a02dae6a3f017 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Wed, 31 Jul 2024 17:40:48 +0300 Subject: [PATCH 15/49] Addressing review comments. --- src/lib/provable/crypto/foreign-curve.ts | 2 +- src/lib/provable/crypto/foreign-ecdsa.ts | 2 +- src/lib/provable/foreign-field.ts | 18 ++++++++++++------ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/lib/provable/crypto/foreign-curve.ts b/src/lib/provable/crypto/foreign-curve.ts index 01067b1c59..4b19b961c4 100644 --- a/src/lib/provable/crypto/foreign-curve.ts +++ b/src/lib/provable/crypto/foreign-curve.ts @@ -42,7 +42,7 @@ class ForeignCurve { /** * Create a new {@link ForeignCurve} from an object representing the (affine) x and y coordinates. * - * Note: Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. Refer to {@link ForeignField} constructor comments for more details. + * Note: Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. Please refer to the {@link ForeignField} constructor comments for more details. * * @example * ```ts diff --git a/src/lib/provable/crypto/foreign-ecdsa.ts b/src/lib/provable/crypto/foreign-ecdsa.ts index 5cbd03427d..88fa94366f 100644 --- a/src/lib/provable/crypto/foreign-ecdsa.ts +++ b/src/lib/provable/crypto/foreign-ecdsa.ts @@ -39,7 +39,7 @@ class EcdsaSignature { /** * Create a new {@link EcdsaSignature} from an object containing the scalars r and s. * - *Note: Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. Refer to {@link ForeignField} constructor comments for more details. + * Note: Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. Please refer to the {@link ForeignField} constructor comments for more details. */ constructor(signature: { r: AlmostForeignField | Field3 | bigint | number; diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts index 8c01366bce..9ff6e33639 100644 --- a/src/lib/provable/foreign-field.ts +++ b/src/lib/provable/foreign-field.ts @@ -8,9 +8,8 @@ import { Field, checkBitLength, withMessage } from './field.js'; import { Provable } from './provable.js'; import { Bool } from './bool.js'; import { Tuple, TupleMap, TupleN } from '../util/types.js'; -import { Field3 } from './gadgets/foreign-field.js'; import { Gadgets } from './gadgets/gadgets.js'; -import { ForeignField as FF } from './gadgets/foreign-field.js'; +import { ForeignField as FF, Field3 } from './gadgets/foreign-field.js'; import { assert } from './gadgets/common.js'; import { l3, l } from './gadgets/range-check.js'; import { ProvablePureExtended } from './types/struct.js'; @@ -101,26 +100,33 @@ class ForeignField { * class SmallField extends createForeignField(17n) {} * class LargerField extends createForeignField(19n) {} * - * let smallField = new SmallField(10); - * let largerField = new LargerField(17); + * let smallField: SmallField = ...; + * let largerField: LargerField = ...; * Gadgets.ForeignField.assertLessThan(largerField.value, SmallField.modulus); * let x = new SmallField(largerField); * ``` * - When constructing from a {@link Field3} array, ensure all elements are valid Field elements and range checked. * @example * ```ts + * class MyField extends createForeignField(20n) {} * let field3Array = [Field(15), Field(0), Field(0)]; - * let x = new ForeignField(field3Array); + * let x = new MyField(field3Array); * ``` * - Ensure constants are correctly reduced to the modulus of the field. * @example * ```ts - * let x = new ForeignField(20); // Automatically reduced to the field's modulus + * class MyField extends createForeignField(10n) {} + * let x = new MyField(10); // Automatically reduced to the field's modulus * ``` */ constructor(x: ForeignField | Field3 | bigint | number | string) { const p = this.modulus; if (x instanceof ForeignField) { + if (x.modulus !== p) { + throw new Error( + `ForeignField constructor: modulus mismatch. Expected ${p}, got ${x.modulus}. Please provide a value with the correct modulus. You can use 'Gadgets.ForeignField.assertLessThan()' to check it.` + ); + } this.value = x.value; return; } From 17e1a1f6cce5ca5ce1ef218b85277b13731f9ed6 Mon Sep 17 00:00:00 2001 From: Coby Date: Wed, 31 Jul 2024 12:29:36 -0400 Subject: [PATCH 16/49] proposed fix for multiple actions issue --- src/lib/mina/fetch.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/lib/mina/fetch.ts b/src/lib/mina/fetch.ts index c9058e36c9..bac7c7bb73 100644 --- a/src/lib/mina/fetch.ts +++ b/src/lib/mina/fetch.ts @@ -369,7 +369,7 @@ async function fetchMissingData( await fetchLastBlock(graphqlEndpoint); await fetchGenesisConstants(graphqlEndpoint); delete networksToFetch[network[0]]; - } catch {} + } catch { } })() ); } @@ -721,7 +721,7 @@ async function fetchActions( if ( fetchedActions.length !== 0 && fetchedActions[0].actionState.actionStateOne === - actionStates.fromActionState + actionStates.fromActionState ) { fetchedActions = fetchedActions.slice(1); } @@ -736,16 +736,23 @@ async function fetchActions( `No action data was found for the account ${publicKey} with the latest action state ${actionState}` ); + // Reverse order of multiple actions from a single block + if (actionData.length > 1) { + actionData = actionData.reverse(); + } + // split actions by account update let actionsByAccountUpdate: string[][][] = []; let currentAccountUpdateId = 'none'; let currentActions: string[][]; actionData.forEach(({ accountUpdateId, data }) => { if (accountUpdateId === currentAccountUpdateId) { - currentActions.push(data); + console.log(data) + currentActions.push(data.reverse()); } else { + console.log(data) currentAccountUpdateId = accountUpdateId; - currentActions = [data]; + currentActions = [data.reverse()]; actionsByAccountUpdate.push(currentActions); } }); From 5b1a071f59d631b7747bf72c9b1f7ca98c15fdd3 Mon Sep 17 00:00:00 2001 From: Coby Date: Wed, 31 Jul 2024 12:30:43 -0400 Subject: [PATCH 17/49] rm erroneous changes --- src/lib/mina/fetch.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib/mina/fetch.ts b/src/lib/mina/fetch.ts index bac7c7bb73..892cf35398 100644 --- a/src/lib/mina/fetch.ts +++ b/src/lib/mina/fetch.ts @@ -747,12 +747,10 @@ async function fetchActions( let currentActions: string[][]; actionData.forEach(({ accountUpdateId, data }) => { if (accountUpdateId === currentAccountUpdateId) { - console.log(data) - currentActions.push(data.reverse()); + currentActions.push(data); } else { - console.log(data) currentAccountUpdateId = accountUpdateId; - currentActions = [data.reverse()]; + currentActions = [data]; actionsByAccountUpdate.push(currentActions); } }); From f498251a64d458638d7466cb7a08e3dd51774b0f Mon Sep 17 00:00:00 2001 From: Coby Date: Wed, 31 Jul 2024 16:00:00 -0400 Subject: [PATCH 18/49] adding a test --- src/lib/fixtures/fetchActionsResponse.ts | 114 +++++++++++++++++++++++ src/lib/mina/fetch.test.ts | 37 ++++++++ src/lib/mina/fetch.ts | 74 +++++++++++---- 3 files changed, 208 insertions(+), 17 deletions(-) create mode 100644 src/lib/fixtures/fetchActionsResponse.ts create mode 100644 src/lib/mina/fetch.test.ts diff --git a/src/lib/fixtures/fetchActionsResponse.ts b/src/lib/fixtures/fetchActionsResponse.ts new file mode 100644 index 0000000000..20e5648b87 --- /dev/null +++ b/src/lib/fixtures/fetchActionsResponse.ts @@ -0,0 +1,114 @@ +export const mockFetchActionsResponse = +{ + "data": { + "actions": [ + { + "blockInfo": { + "distanceFromMaxBlockHeight": 11 + }, + "actionState": { + "actionStateOne": "25525130517416993227046681664758665799110129890808721833148757111140891481208", + "actionStateTwo": "25079927036070901246064867767436987657692091363973573142121686150614948079097" + }, + "actionData": [ + { + "accountUpdateId": "5", + "data": [ + "20503089751358270987184701275168489753952341816059774976784079526478451099801", + "1" + ] + }, + { + "accountUpdateId": "3", + "data": [ + "20374659537065244088703638031937922870146667362923279084491778322749365537089", + "1" + ] + } + ] + }, + { + "blockInfo": { + "distanceFromMaxBlockHeight": 5 + }, + "actionState": { + "actionStateOne": "290963518424616502946790040851348455652296009700336010663574777600482385855", + "actionStateTwo": "25525130517416993227046681664758665799110129890808721833148757111140891481208" + }, + "actionData": [ + { + "accountUpdateId": "7", + "data": [ + "3374074164183544078218789545772953663729921088152354292852793744356608231707", + "0" + ] + } + ] + }, + { + "blockInfo": { + "distanceFromMaxBlockHeight": 3 + }, + "actionState": { + "actionStateOne": "20673199655841577810393943638910551364027795297920791498278816237738641857371", + "actionStateTwo": "290963518424616502946790040851348455652296009700336010663574777600482385855" + }, + "actionData": [ + { + "accountUpdateId": "9", + "data": [ + "12630758077588166643924428865613845067150916064939816120404808842510620524633", + "1" + ] + } + ] + }, + { + "blockInfo": { + "distanceFromMaxBlockHeight": 2 + }, + "actionState": { + "actionStateOne": "10964420428484427410756859799314206378989718180435238943573393516522086219419", + "actionStateTwo": "20673199655841577810393943638910551364027795297920791498278816237738641857371" + }, + "actionData": [ + { + "accountUpdateId": "19", + "data": [ + "17137397755795687855356639427474789131368991089558570411893673365904353943290", + "1" + ] + }, + { + "accountUpdateId": "17", + "data": [ + "3378367318331499715304980508337843233019278703665446829424824679144818589558", + "1" + ] + }, + { + "accountUpdateId": "15", + "data": [ + "27263309408256888453299195755797013857604561285332380691270111409680109142128", + "1" + ] + }, + { + "accountUpdateId": "13", + "data": [ + "15789351988619560045401465240113496854401074115453702466673859303925517061263", + "0" + ] + }, + { + "accountUpdateId": "11", + "data": [ + "5643224648393140391519847064914429159616501351124129591669928700148350171602", + "0" + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/src/lib/mina/fetch.test.ts b/src/lib/mina/fetch.test.ts new file mode 100644 index 0000000000..3ea687220a --- /dev/null +++ b/src/lib/mina/fetch.test.ts @@ -0,0 +1,37 @@ +import { PrivateKey, TokenId } from 'o1js'; +import { createActionsList } from './fetch.js'; +import { mockFetchActionsResponse } from '../fixtures/fetchActionsResponse.js'; + +describe('Fetch', () => { + describe('#createActionsList', () => { + let actionsList: any; + + describe('with default params', () => { + beforeEach(async () => { + const defaultPublicKey = PrivateKey.random().toPublicKey().toBase58(); + const defaultActionStates = { fromActionState: undefined, endActionState: undefined } + const defaultAccountInfo = { + publicKey: defaultPublicKey, + actionStates: defaultActionStates, + tokenId: TokenId.default.toString() + } + + actionsList = createActionsList(defaultAccountInfo, mockFetchActionsResponse.data.actions); + }); + + it('orders the actions correctly', () => { + expect(actionsList).toMatchObject([ + { "actions": [["20374659537065244088703638031937922870146667362923279084491778322749365537089", "1"]], "hash": "10619825168606131449407092474314250900469658818945385329390497057469974757422" }, + { "actions": [["20503089751358270987184701275168489753952341816059774976784079526478451099801", "1"]], "hash": "25525130517416993227046681664758665799110129890808721833148757111140891481208" }, + { "actions": [["3374074164183544078218789545772953663729921088152354292852793744356608231707", "0"]], "hash": "290963518424616502946790040851348455652296009700336010663574777600482385855" }, + { "actions": [["12630758077588166643924428865613845067150916064939816120404808842510620524633", "1"]], "hash": "20673199655841577810393943638910551364027795297920791498278816237738641857371" }, + { "actions": [["5643224648393140391519847064914429159616501351124129591669928700148350171602", "0"]], "hash": "5284016523143033193387918577616839424871122381326995145988133445906503263869" }, + { "actions": [["15789351988619560045401465240113496854401074115453702466673859303925517061263", "0"]], "hash": "16944163018367910067334012882171366051616125936127175065464614786387687317044" }, + { "actions": [["27263309408256888453299195755797013857604561285332380691270111409680109142128", "1"]], "hash": "23662159967366296714544063539035629952291787828104373633198732070740691309118" }, + { "actions": [["3378367318331499715304980508337843233019278703665446829424824679144818589558", "1"]], "hash": "1589729766029695153975344283092689798747741638003354620355672853210932754595" }, + { "actions": [["17137397755795687855356639427474789131368991089558570411893673365904353943290", "1"]], "hash": "10964420428484427410756859799314206378989718180435238943573393516522086219419" } + ]) + }); + }); + }); +}); \ No newline at end of file diff --git a/src/lib/mina/fetch.ts b/src/lib/mina/fetch.ts index 892cf35398..df148ab959 100644 --- a/src/lib/mina/fetch.ts +++ b/src/lib/mina/fetch.ts @@ -64,6 +64,7 @@ export { sendZkapp, fetchEvents, fetchActions, + makeGraphqlRequest, Lightnet, type GenesisConstants, type ActionStatesStringified, @@ -77,6 +78,26 @@ type NetworkConfig = { lightnetAccountManagerEndpoint: string; }; +type ActionsQueryInputs = { + publicKey: string; + actionStates: ActionStatesStringified; + tokenId?: string; +} + +type FetchedAction = { + blockInfo: { + distanceFromMaxBlockHeight: number; + }; + actionState: { + actionStateOne: string; + actionStateTwo: string; + }; + actionData: { + accountUpdateId: string; + data: string[]; + }[]; +} + let networkConfig = { minaEndpoint: '', minaFallbackEndpoints: [] as string[], @@ -684,11 +705,7 @@ async function fetchEvents( } async function fetchActions( - accountInfo: { - publicKey: string; - actionStates: ActionStatesStringified; - tokenId?: string; - }, + accountInfo: ActionsQueryInputs, graphqlEndpoint = networkConfig.archiveEndpoint ) { if (!graphqlEndpoint) @@ -700,13 +717,8 @@ async function fetchActions( actionStates, tokenId = TokenId.toBase58(TokenId.default), } = accountInfo; - let [response, error] = await makeGraphqlRequest( - getActionsQuery(publicKey, actionStates, tokenId), - graphqlEndpoint, - networkConfig.archiveFallbackEndpoints - ); - if (error) throw Error(error.statusText); - let fetchedActions = response?.data.actions; + + let fetchedActions = await makeFetchActionsGraphqlRequest(accountInfo, graphqlEndpoint); if (fetchedActions === undefined) { return { error: { @@ -716,6 +728,37 @@ async function fetchActions( }; } + const actionsList = createActionsList(accountInfo, fetchedActions); + addCachedActions({ publicKey, tokenId }, actionsList, graphqlEndpoint); + + return actionsList; +} + +async function makeFetchActionsGraphqlRequest(accountInfo: ActionsQueryInputs, graphqlEndpoint: string) { + const { + publicKey, + actionStates, + tokenId = TokenId.toBase58(TokenId.default), + } = accountInfo; + + let [response, error] = await makeGraphqlRequest( + getActionsQuery(publicKey, actionStates, tokenId), + graphqlEndpoint, + networkConfig.archiveFallbackEndpoints + ); + if (error) throw Error(error.statusText); + let fetchedActions = response?.data.actions; + + return fetchedActions; +} + +export function createActionsList(accountInfo: ActionsQueryInputs, fetchedActions: FetchedAction[]) { + const { + publicKey, + actionStates, + tokenId = TokenId.toBase58(TokenId.default), + } = accountInfo; + let actionsList: { actions: string[][]; hash: string }[] = []; // correct for archive node sending one block too many if ( @@ -736,10 +779,8 @@ async function fetchActions( `No action data was found for the account ${publicKey} with the latest action state ${actionState}` ); - // Reverse order of multiple actions from a single block - if (actionData.length > 1) { - actionData = actionData.reverse(); - } + // Reverse order so that multiple actions in the same block get replayed in reverse + actionData = actionData.reverse(); // split actions by account update let actionsByAccountUpdate: string[][][] = []; @@ -774,7 +815,6 @@ async function fetchActions( ); } }); - addCachedActions({ publicKey, tokenId }, actionsList, graphqlEndpoint); return actionsList; } From 9d6fbec5b41fa9ae6c0940a59378f51b510a9cf1 Mon Sep 17 00:00:00 2001 From: Coby Date: Wed, 31 Jul 2024 16:05:32 -0400 Subject: [PATCH 19/49] clean up --- src/lib/mina/fetch.ts | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/lib/mina/fetch.ts b/src/lib/mina/fetch.ts index df148ab959..60976538ae 100644 --- a/src/lib/mina/fetch.ts +++ b/src/lib/mina/fetch.ts @@ -718,7 +718,13 @@ async function fetchActions( tokenId = TokenId.toBase58(TokenId.default), } = accountInfo; - let fetchedActions = await makeFetchActionsGraphqlRequest(accountInfo, graphqlEndpoint); + let [response, error] = await makeGraphqlRequest( + getActionsQuery(publicKey, actionStates, tokenId), + graphqlEndpoint, + networkConfig.archiveFallbackEndpoints + ); + if (error) throw Error(error.statusText); + let fetchedActions = response?.data.actions; if (fetchedActions === undefined) { return { error: { @@ -734,29 +740,13 @@ async function fetchActions( return actionsList; } -async function makeFetchActionsGraphqlRequest(accountInfo: ActionsQueryInputs, graphqlEndpoint: string) { - const { - publicKey, - actionStates, - tokenId = TokenId.toBase58(TokenId.default), - } = accountInfo; - - let [response, error] = await makeGraphqlRequest( - getActionsQuery(publicKey, actionStates, tokenId), - graphqlEndpoint, - networkConfig.archiveFallbackEndpoints - ); - if (error) throw Error(error.statusText); - let fetchedActions = response?.data.actions; - - return fetchedActions; -} - +/** + * Given a graphQL response from #getActionsQuery, process the actions into a canonical actions list + */ export function createActionsList(accountInfo: ActionsQueryInputs, fetchedActions: FetchedAction[]) { const { publicKey, actionStates, - tokenId = TokenId.toBase58(TokenId.default), } = accountInfo; let actionsList: { actions: string[][]; hash: string }[] = []; From 5925904552ca59984aa31576f5fd9651f1b221fe Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Thu, 1 Aug 2024 10:53:57 +0300 Subject: [PATCH 20/49] Addressing review comments. --- src/lib/provable/foreign-field.ts | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts index 9ff6e33639..9bc3430df9 100644 --- a/src/lib/provable/foreign-field.ts +++ b/src/lib/provable/foreign-field.ts @@ -13,6 +13,7 @@ import { ForeignField as FF, Field3 } from './gadgets/foreign-field.js'; import { assert } from './gadgets/common.js'; import { l3, l } from './gadgets/range-check.js'; import { ProvablePureExtended } from './types/struct.js'; +import { isField } from './core/field-constructor.js'; // external API export { createForeignField }; @@ -95,29 +96,8 @@ class ForeignField { * Note: Inputs must be range checked if they originate from a different field with a different modulus or if they are not constants. * * - When constructing from another {@link ForeignField} instance, ensure the modulus matches. If not, check the modulus using `Gadgets.ForeignField.assertLessThan()` and handle appropriately. - * @example - * ```ts - * class SmallField extends createForeignField(17n) {} - * class LargerField extends createForeignField(19n) {} - * - * let smallField: SmallField = ...; - * let largerField: LargerField = ...; - * Gadgets.ForeignField.assertLessThan(largerField.value, SmallField.modulus); - * let x = new SmallField(largerField); - * ``` * - When constructing from a {@link Field3} array, ensure all elements are valid Field elements and range checked. - * @example - * ```ts - * class MyField extends createForeignField(20n) {} - * let field3Array = [Field(15), Field(0), Field(0)]; - * let x = new MyField(field3Array); - * ``` * - Ensure constants are correctly reduced to the modulus of the field. - * @example - * ```ts - * class MyField extends createForeignField(10n) {} - * let x = new MyField(10); // Automatically reduced to the field's modulus - * ``` */ constructor(x: ForeignField | Field3 | bigint | number | string) { const p = this.modulus; @@ -132,6 +112,11 @@ class ForeignField { } // Field3 if (Array.isArray(x)) { + if (x.some((limb) => !isField(limb))) { + throw new Error( + `ForeignField constructor: invalid 'Field3' element. Please provide valid Field elements.` + ); + } this.value = x; return; } From fe63c18797e6efa2063fc9b1a0f5ebc3c011a7ef Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Thu, 1 Aug 2024 14:11:08 +0300 Subject: [PATCH 21/49] Addressing review comments. --- src/lib/provable/foreign-field.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/lib/provable/foreign-field.ts b/src/lib/provable/foreign-field.ts index 9bc3430df9..7e87dd7eed 100644 --- a/src/lib/provable/foreign-field.ts +++ b/src/lib/provable/foreign-field.ts @@ -13,7 +13,6 @@ import { ForeignField as FF, Field3 } from './gadgets/foreign-field.js'; import { assert } from './gadgets/common.js'; import { l3, l } from './gadgets/range-check.js'; import { ProvablePureExtended } from './types/struct.js'; -import { isField } from './core/field-constructor.js'; // external API export { createForeignField }; @@ -112,11 +111,6 @@ class ForeignField { } // Field3 if (Array.isArray(x)) { - if (x.some((limb) => !isField(limb))) { - throw new Error( - `ForeignField constructor: invalid 'Field3' element. Please provide valid Field elements.` - ); - } this.value = x; return; } From dce77b1c28e242ba93dfef5eb935d4607d8c1e9e Mon Sep 17 00:00:00 2001 From: Coby Date: Thu, 1 Aug 2024 10:02:51 -0400 Subject: [PATCH 22/49] formatting changes from PR review --- src/lib/mina/fetch.test.ts | 37 ------ src/lib/mina/fetch.ts | 31 ++--- src/lib/mina/fetch.unit-test.ts | 111 ++++++++++++++++++ .../fixtures/fetchActionsResponse.ts | 0 src/lib/mina/graphql.ts | 28 +++-- 5 files changed, 135 insertions(+), 72 deletions(-) delete mode 100644 src/lib/mina/fetch.test.ts rename src/lib/{ => mina}/fixtures/fetchActionsResponse.ts (100%) diff --git a/src/lib/mina/fetch.test.ts b/src/lib/mina/fetch.test.ts deleted file mode 100644 index 3ea687220a..0000000000 --- a/src/lib/mina/fetch.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { PrivateKey, TokenId } from 'o1js'; -import { createActionsList } from './fetch.js'; -import { mockFetchActionsResponse } from '../fixtures/fetchActionsResponse.js'; - -describe('Fetch', () => { - describe('#createActionsList', () => { - let actionsList: any; - - describe('with default params', () => { - beforeEach(async () => { - const defaultPublicKey = PrivateKey.random().toPublicKey().toBase58(); - const defaultActionStates = { fromActionState: undefined, endActionState: undefined } - const defaultAccountInfo = { - publicKey: defaultPublicKey, - actionStates: defaultActionStates, - tokenId: TokenId.default.toString() - } - - actionsList = createActionsList(defaultAccountInfo, mockFetchActionsResponse.data.actions); - }); - - it('orders the actions correctly', () => { - expect(actionsList).toMatchObject([ - { "actions": [["20374659537065244088703638031937922870146667362923279084491778322749365537089", "1"]], "hash": "10619825168606131449407092474314250900469658818945385329390497057469974757422" }, - { "actions": [["20503089751358270987184701275168489753952341816059774976784079526478451099801", "1"]], "hash": "25525130517416993227046681664758665799110129890808721833148757111140891481208" }, - { "actions": [["3374074164183544078218789545772953663729921088152354292852793744356608231707", "0"]], "hash": "290963518424616502946790040851348455652296009700336010663574777600482385855" }, - { "actions": [["12630758077588166643924428865613845067150916064939816120404808842510620524633", "1"]], "hash": "20673199655841577810393943638910551364027795297920791498278816237738641857371" }, - { "actions": [["5643224648393140391519847064914429159616501351124129591669928700148350171602", "0"]], "hash": "5284016523143033193387918577616839424871122381326995145988133445906503263869" }, - { "actions": [["15789351988619560045401465240113496854401074115453702466673859303925517061263", "0"]], "hash": "16944163018367910067334012882171366051616125936127175065464614786387687317044" }, - { "actions": [["27263309408256888453299195755797013857604561285332380691270111409680109142128", "1"]], "hash": "23662159967366296714544063539035629952291787828104373633198732070740691309118" }, - { "actions": [["3378367318331499715304980508337843233019278703665446829424824679144818589558", "1"]], "hash": "1589729766029695153975344283092689798747741638003354620355672853210932754595" }, - { "actions": [["17137397755795687855356639427474789131368991089558570411893673365904353943290", "1"]], "hash": "10964420428484427410756859799314206378989718180435238943573393516522086219419" } - ]) - }); - }); - }); -}); \ No newline at end of file diff --git a/src/lib/mina/fetch.ts b/src/lib/mina/fetch.ts index 60976538ae..d3bcce732f 100644 --- a/src/lib/mina/fetch.ts +++ b/src/lib/mina/fetch.ts @@ -17,6 +17,7 @@ import { type LastBlockQueryResponse, type GenesisConstantsResponse, type LastBlockQueryFailureCheckResponse, + type FetchedAction, type FetchedBlock, type TransactionStatus, type TransactionStatusQueryResponse, @@ -82,21 +83,7 @@ type ActionsQueryInputs = { publicKey: string; actionStates: ActionStatesStringified; tokenId?: string; -} - -type FetchedAction = { - blockInfo: { - distanceFromMaxBlockHeight: number; - }; - actionState: { - actionStateOne: string; - actionStateTwo: string; - }; - actionData: { - accountUpdateId: string; - data: string[]; - }[]; -} +}; let networkConfig = { minaEndpoint: '', @@ -390,7 +377,7 @@ async function fetchMissingData( await fetchLastBlock(graphqlEndpoint); await fetchGenesisConstants(graphqlEndpoint); delete networksToFetch[network[0]]; - } catch { } + } catch {} })() ); } @@ -743,18 +730,18 @@ async function fetchActions( /** * Given a graphQL response from #getActionsQuery, process the actions into a canonical actions list */ -export function createActionsList(accountInfo: ActionsQueryInputs, fetchedActions: FetchedAction[]) { - const { - publicKey, - actionStates, - } = accountInfo; +export function createActionsList( + accountInfo: ActionsQueryInputs, + fetchedActions: FetchedAction[] +) { + const { publicKey, actionStates } = accountInfo; let actionsList: { actions: string[][]; hash: string }[] = []; // correct for archive node sending one block too many if ( fetchedActions.length !== 0 && fetchedActions[0].actionState.actionStateOne === - actionStates.fromActionState + actionStates.fromActionState ) { fetchedActions = fetchedActions.slice(1); } diff --git a/src/lib/mina/fetch.unit-test.ts b/src/lib/mina/fetch.unit-test.ts index e61a7155a6..5678eee9d6 100644 --- a/src/lib/mina/fetch.unit-test.ts +++ b/src/lib/mina/fetch.unit-test.ts @@ -1,3 +1,7 @@ +import { PrivateKey, TokenId } from 'o1js'; +import { createActionsList } from './fetch.js'; +import { mockFetchActionsResponse } from './fixtures/fetchActionsResponse.js'; +import { test, describe } from 'node:test'; import { removeJsonQuotes } from './graphql.js'; import { expect } from 'expect'; @@ -118,3 +122,110 @@ actual = removeJsonQuotes(input); expect(actual).toEqual(expected); console.log('regex tests complete 🎉'); + +describe('Fetch', async (t) => { + describe('#createActionsList with default params', async (t) => { + const defaultPublicKey = PrivateKey.random().toPublicKey().toBase58(); + const defaultActionStates = { + fromActionState: undefined, + endActionState: undefined, + }; + const defaultAccountInfo = { + publicKey: defaultPublicKey, + actionStates: defaultActionStates, + tokenId: TokenId.default.toString(), + }; + + const actionsList = createActionsList( + defaultAccountInfo, + mockFetchActionsResponse.data.actions + ); + + await test('orders the actions correctly', async () => { + expect(actionsList).toEqual([ + { + actions: [ + [ + '20374659537065244088703638031937922870146667362923279084491778322749365537089', + '1', + ], + ], + hash: '10619825168606131449407092474314250900469658818945385329390497057469974757422', + }, + { + actions: [ + [ + '20503089751358270987184701275168489753952341816059774976784079526478451099801', + '1', + ], + ], + hash: '25525130517416993227046681664758665799110129890808721833148757111140891481208', + }, + { + actions: [ + [ + '3374074164183544078218789545772953663729921088152354292852793744356608231707', + '0', + ], + ], + hash: '290963518424616502946790040851348455652296009700336010663574777600482385855', + }, + { + actions: [ + [ + '12630758077588166643924428865613845067150916064939816120404808842510620524633', + '1', + ], + ], + hash: '20673199655841577810393943638910551364027795297920791498278816237738641857371', + }, + { + actions: [ + [ + '5643224648393140391519847064914429159616501351124129591669928700148350171602', + '0', + ], + ], + hash: '5284016523143033193387918577616839424871122381326995145988133445906503263869', + }, + { + actions: [ + [ + '15789351988619560045401465240113496854401074115453702466673859303925517061263', + '0', + ], + ], + hash: '16944163018367910067334012882171366051616125936127175065464614786387687317044', + }, + { + actions: [ + [ + '27263309408256888453299195755797013857604561285332380691270111409680109142128', + '1', + ], + ], + hash: '23662159967366296714544063539035629952291787828104373633198732070740691309118', + }, + { + actions: [ + [ + '3378367318331499715304980508337843233019278703665446829424824679144818589558', + '1', + ], + ], + hash: '1589729766029695153975344283092689798747741638003354620355672853210932754595', + }, + { + actions: [ + [ + '17137397755795687855356639427474789131368991089558570411893673365904353943290', + '1', + ], + ], + hash: '10964420428484427410756859799314206378989718180435238943573393516522086219419', + }, + ]); + }); + }); +}); +``; diff --git a/src/lib/fixtures/fetchActionsResponse.ts b/src/lib/mina/fixtures/fetchActionsResponse.ts similarity index 100% rename from src/lib/fixtures/fetchActionsResponse.ts rename to src/lib/mina/fixtures/fetchActionsResponse.ts diff --git a/src/lib/mina/graphql.ts b/src/lib/mina/graphql.ts index 5d20a2c71b..fd39322898 100644 --- a/src/lib/mina/graphql.ts +++ b/src/lib/mina/graphql.ts @@ -250,22 +250,24 @@ type EventQueryResponse = { }[]; }; -type ActionQueryResponse = { - actions: { - blockInfo: { - distanceFromMaxBlockHeight: number; - }; - actionState: { - actionStateOne: string; - actionStateTwo: string; - }; - actionData: { - accountUpdateId: string; - data: string[]; - }[]; +export type FetchedAction = { + blockInfo: { + distanceFromMaxBlockHeight: number; + }; + actionState: { + actionStateOne: string; + actionStateTwo: string; + }; + actionData: { + accountUpdateId: string; + data: string[]; }[]; }; +type ActionQueryResponse = { + actions: FetchedAction[]; +}; + type EventActionFilterOptions = { to?: UInt32; from?: UInt32; From d2c9320eb44cd9157f8e2059c325292c22f4a84e Mon Sep 17 00:00:00 2001 From: Coby Date: Fri, 2 Aug 2024 13:58:52 -0400 Subject: [PATCH 23/49] trying sort order based on account update id --- run-ci-live-tests.sh | 9 ++ run-ci-tests.sh | 3 +- src/examples/utils/randomAccounts.ts | 19 +++ .../actions-as-merkle-list-iterator.ts | 77 +++++----- .../zkapps/reducer/actions-as-merkle-list.ts | 80 +++++----- src/examples/zkapps/reducer/run-live.ts | 143 ++++++++++++++++++ src/examples/zkapps/reducer/run.ts | 5 + src/lib/mina/fetch.ts | 6 +- src/lib/mina/graphql.ts | 3 +- src/lib/mina/mina.ts | 2 +- 10 files changed, 269 insertions(+), 78 deletions(-) create mode 100644 src/examples/utils/randomAccounts.ts create mode 100644 src/examples/zkapps/reducer/run-live.ts create mode 100644 src/examples/zkapps/reducer/run.ts diff --git a/run-ci-live-tests.sh b/run-ci-live-tests.sh index 3d6a53de9c..1355b00208 100755 --- a/run-ci-live-tests.sh +++ b/run-ci-live-tests.sh @@ -21,6 +21,8 @@ DEX_PROC=$! FETCH_PROC=$! ./run src/tests/transaction-flow.ts --bundle | add_prefix "TRANSACTION_FLOW" & TRANSACTION_FLOW_PROC=$! +./run src/examples/reducer/run-live.ts --bundle | add_prefix "REDUCER" & +REDUCER_FLOW_PROC=$! # Wait for each process and capture their exit statuses FAILURE=0 @@ -52,6 +54,13 @@ if [ $? -ne 0 ]; then echo "" FAILURE=1 fi +wait $REDUCER_FLOW_PROC +if [ $? -ne 0 ]; then + echo "" + echo "REDUCER_FLOW test failed." + echo "" + FAILURE=1 +fi # Exit with failure if any process failed if [ $FAILURE -ne 0 ]; then diff --git a/run-ci-tests.sh b/run-ci-tests.sh index c5153b514e..263ceee16f 100755 --- a/run-ci-tests.sh +++ b/run-ci-tests.sh @@ -14,8 +14,7 @@ case $TEST_TYPE in "Reducer integration tests") echo "Running reducer integration tests" - ./run src/examples/zkapps/reducer/actions-as-merkle-list.ts --bundle - ./run src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts --bundle + ./run src/examples/zkapps/reducer/run.ts --bundle ;; "Voting integration tests") diff --git a/src/examples/utils/randomAccounts.ts b/src/examples/utils/randomAccounts.ts new file mode 100644 index 0000000000..2b43a511d6 --- /dev/null +++ b/src/examples/utils/randomAccounts.ts @@ -0,0 +1,19 @@ +import { PrivateKey, PublicKey } from '../../../dist/node/index.js'; + +/** + * Predefined accounts keys, labeled by the input strings. Useful for testing/debugging with consistent keys. + */ +export function randomAccounts( + ...names: [K, ...K[]] +): { keys: Record; addresses: Record } { + let base58Keys = Array(names.length) + .fill('') + .map(() => PrivateKey.random().toBase58()); + let keys = Object.fromEntries( + names.map((name, idx) => [name, PrivateKey.fromBase58(base58Keys[idx])]) + ) as Record; + let addresses = Object.fromEntries( + names.map((name) => [name, keys[name].toPublicKey()]) + ) as Record; + return { keys, addresses }; +} diff --git a/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts b/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts index 0f7b7c4a23..0400fbd6b9 100644 --- a/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts +++ b/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts @@ -4,11 +4,11 @@ * * This is mainly intended as an example for using `Iterator` and `MerkleList`, but it might also be useful as * a blueprint for processing actions in a custom and more explicit way. - * - * Warning: The reducer API in o1js is currently not safe to use in production applications. The `reduce()` - * method breaks if more than the hard-coded number (default: 32) of actions are pending. Work is actively + * + * Warning: The reducer API in o1js is currently not safe to use in production applications. The `reduce()` + * method breaks if more than the hard-coded number (default: 32) of actions are pending. Work is actively * in progress to mitigate this limitation. - */ + */ import { Field, Mina, @@ -18,7 +18,9 @@ import { SmartContract, method, assert, -} from 'o1js'; +} from '../../../../dist/node/index.js'; + +export { ActionsContract, testLocal }; // constants for our static-sized provable code const MAX_UPDATES_WITH_ACTIONS = 100; @@ -81,44 +83,49 @@ class ActionsContract extends SmartContract { // TESTS -// set up a local blockchain +async function testLocal() { + // set up a local blockchain -let Local = await Mina.LocalBlockchain({ proofsEnabled: false }); -Mina.setActiveInstance(Local); + let Local = await Mina.LocalBlockchain({ proofsEnabled: true }); + Mina.setActiveInstance(Local); -let [sender, contractAddress] = Local.testAccounts; + let [sender, contractAddress] = Local.testAccounts.slice(4); -let contract = new ActionsContract(contractAddress); + let contract = new ActionsContract(contractAddress); -// deploy the contract + // deploy the contract -await ActionsContract.compile(); -console.log( - `rows for ${MAX_UPDATES_WITH_ACTIONS} updates with actions`, - (await ActionsContract.analyzeMethods()).accumulate.rows -); -let deployTx = await Mina.transaction(sender, async () => contract.deploy()); -await deployTx.sign([sender.key, contractAddress.key]).send(); + await ActionsContract.compile(); + console.log( + `rows for ${MAX_UPDATES_WITH_ACTIONS} updates with actions`, + (await ActionsContract.analyzeMethods()).accumulate.rows + ); + let deployTx = await Mina.transaction(sender, async () => contract.deploy()); + await deployTx.sign([sender.key, contractAddress.key]).send(); -// push some actions + // push some actions -let dispatchTx = await Mina.transaction(sender, async () => { - await contract.increment(Field(1)); - await contract.increment(Field(3)); - await contract.increment(Field(5)); - await contract.increment(Field(9)); - await contract.twoIncrements(Field(18), Field(19)); -}); -await dispatchTx.prove(); -await dispatchTx.sign([sender.key]).send(); + let dispatchTx = await Mina.transaction(sender, async () => { + await contract.increment(Field(1)); + await contract.increment(Field(3)); + await contract.increment(Field(5)); + await contract.increment(Field(9)); + await contract.twoIncrements(Field(18), Field(19)); + }); + await dispatchTx.prove(); + await dispatchTx.sign([sender.key]).send(); -assert(contract.reducer.getActions().data.get().length === 5); + assert(contract.reducer.getActions().data.get().length === 5); -// accumulate actions + // accumulate actions -Local.setProofsEnabled(true); -let accTx = await Mina.transaction(sender, () => contract.accumulate()); -await accTx.prove(); -await accTx.sign([sender.key]).send(); + Local.setProofsEnabled(true); + let accTx = await Mina.transaction(sender, () => contract.accumulate()); + await accTx.prove(); + await accTx.sign([sender.key]).send(); -assert(contract.counter.get().toBigInt() === 55n); + assert(contract.counter.get().toBigInt() === 55n); + + await new Promise((_r) => setTimeout(_r, 5000)); + Mina.setActiveInstance(Mina.default); +} diff --git a/src/examples/zkapps/reducer/actions-as-merkle-list.ts b/src/examples/zkapps/reducer/actions-as-merkle-list.ts index 1eb813dbb5..33e37fd9a3 100644 --- a/src/examples/zkapps/reducer/actions-as-merkle-list.ts +++ b/src/examples/zkapps/reducer/actions-as-merkle-list.ts @@ -4,11 +4,11 @@ * * This is mainly intended as an example for using `MerkleList`, but it might also be useful as * a blueprint for processing actions in a custom and more explicit way. - * - * Warning: The reducer API in o1js is currently not safe to use in production applications. The `reduce()` - * method breaks if more than the hard-coded number (default: 32) of actions are pending. Work is actively + * + * Warning: The reducer API in o1js is currently not safe to use in production applications. The `reduce()` + * method breaks if more than the hard-coded number (default: 32) of actions are pending. Work is actively * in progress to mitigate this limitation. - */ + */ import { Bool, Mina, @@ -17,7 +17,9 @@ import { SmartContract, method, assert, -} from 'o1js'; +} from '../../../../dist/node/index.js'; + +export { MerkleListReducing, testLocal }; // in this example, an action is just a public key type Action = PublicKey; @@ -64,7 +66,6 @@ class MerkleListReducing extends SmartContract { hasAddress = hasAddress.or(action.equals(address)); } } - assert(actions.isEmpty()); // we processed all actions assert(hasAddress); // we found the address } @@ -74,43 +75,48 @@ class MerkleListReducing extends SmartContract { // set up a local blockchain -let Local = await Mina.LocalBlockchain({ proofsEnabled: false }); -Mina.setActiveInstance(Local); +async function testLocal() { + let Local = await Mina.LocalBlockchain({ proofsEnabled: true }); + Mina.setActiveInstance(Local); + + let [sender, contractAccount, otherAddress, anotherAddress] = + Local.testAccounts; -let [sender, contractAccount, otherAddress, anotherAddress] = - Local.testAccounts; + let contract = new MerkleListReducing(contractAccount); -let contract = new MerkleListReducing(contractAccount); + // deploy the contract -// deploy the contract + await MerkleListReducing.compile(); + console.log( + `rows for ${MAX_UPDATES_WITH_ACTIONS} updates with actions`, + (await MerkleListReducing.analyzeMethods()).assertContainsAddress.rows + ); + let deployTx = await Mina.transaction(sender, async () => contract.deploy()); + await deployTx.sign([sender.key, contractAccount.key]).send(); -await MerkleListReducing.compile(); -console.log( - `rows for ${MAX_UPDATES_WITH_ACTIONS} updates with actions`, - (await MerkleListReducing.analyzeMethods()).assertContainsAddress.rows -); -let deployTx = await Mina.transaction(sender, async () => contract.deploy()); -await deployTx.sign([sender.key, contractAccount.key]).send(); + // push some actions -// push some actions + let dispatchTx = await Mina.transaction(sender, async () => { + await contract.postAddress(otherAddress); + await contract.postAddress(contractAccount); + await contract.postTwoAddresses(anotherAddress, sender); + await contract.postAddress(anotherAddress); + await contract.postTwoAddresses(contractAccount, otherAddress); + }); + await dispatchTx.prove(); + await dispatchTx.sign([sender.key]).send(); -let dispatchTx = await Mina.transaction(sender, async () => { - await contract.postAddress(otherAddress); - await contract.postAddress(contractAccount); - await contract.postTwoAddresses(anotherAddress, sender); - await contract.postAddress(anotherAddress); - await contract.postTwoAddresses(contractAccount, otherAddress); -}); -await dispatchTx.prove(); -await dispatchTx.sign([sender.key]).send(); + assert(contract.reducer.getActions().data.get().length === 5); -assert(contract.reducer.getActions().data.get().length === 5); + // check if the actions contain the `sender` address -// check if the actions contain the `sender` address + Local.setProofsEnabled(true); + let containsTx = await Mina.transaction(sender, () => + contract.assertContainsAddress(sender) + ); + await containsTx.prove(); + await containsTx.sign([sender.key]).send(); -Local.setProofsEnabled(true); -let containsTx = await Mina.transaction(sender, () => - contract.assertContainsAddress(sender) -); -await containsTx.prove(); -await containsTx.sign([sender.key]).send(); + await new Promise((_r) => setTimeout(_r, 5000)); + Mina.setActiveInstance(Mina.default); +} diff --git a/src/examples/zkapps/reducer/run-live.ts b/src/examples/zkapps/reducer/run-live.ts new file mode 100644 index 0000000000..4cbfe77db4 --- /dev/null +++ b/src/examples/zkapps/reducer/run-live.ts @@ -0,0 +1,143 @@ +import { + AccountUpdate, + Lightnet, + Mina, + PrivateKey, +} from '../../../../dist/node/index.js'; +import { randomAccounts } from '../../utils/randomAccounts.js'; +import { tic, toc } from '../../utils/tic-toc.node.js'; +import { MerkleListReducing } from './actions-as-merkle-list.js'; + +const useCustomLocalNetwork = process.env.USE_CUSTOM_LOCAL_NETWORK === 'true'; + +if (!useCustomLocalNetwork) { + throw 'Only Lightnet is currently supported'; +} + +tic('Run reducer examples against real network.'); +console.log(); +const network = Mina.Network({ + mina: useCustomLocalNetwork + ? 'http://localhost:8080/graphql' + : 'https://berkeley.minascan.io/graphql', + archive: useCustomLocalNetwork + ? 'http://localhost:8282' + : 'https://api.minascan.io/archive/berkeley/v1/graphql', + lightnetAccountManager: 'http://localhost:8181', +}); +Mina.setActiveInstance(network); + +let { keys, addresses } = randomAccounts('contract', 'user1', 'user2'); + +let tx, pendingTx: Mina.PendingTransaction, balances, oldBalances; + +// compile contracts & wait for fee payer to be funded +const senderKey = useCustomLocalNetwork + ? (await Lightnet.acquireKeyPair()).privateKey + : PrivateKey.random(); +const sender = senderKey.toPublicKey(); + +const sender2Key = useCustomLocalNetwork + ? (await Lightnet.acquireKeyPair()).privateKey + : PrivateKey.random(); +const sender2 = sender2Key.toPublicKey(); + +tic('Compiling Merkle List Reducer Smart Contract'); +await MerkleListReducing.compile(); +toc(); +const merkleListReducerContract = new MerkleListReducing(addresses.contract); + +let senderSpec = { sender, fee: 1000000000n }; +let sender2Spec = { sender: sender2, fee: 1000000000n }; + +tic('deploy contracts'); +let deployTx = await Mina.transaction(senderSpec, async () => { + AccountUpdate.createSigned(sender).balance.subInPlace( + Mina.getNetworkConstants().accountCreationFee + ); + await merkleListReducerContract.deploy(); +}); +pendingTx = await deployTx.sign([senderKey, keys.contract]).send(); + +await pendingTx.wait(); +toc('deploy contracts'); +console.log('working so far'); +// push some actions + +tic('dispatch transactions'); +let dispatchTx = await Mina.transaction(senderSpec, async () => { + await merkleListReducerContract.postAddress(addresses.user1); + await merkleListReducerContract.postAddress(addresses.contract); + await merkleListReducerContract.postTwoAddresses(addresses.user2, sender); + await merkleListReducerContract.postAddress(addresses.user2); + await merkleListReducerContract.postTwoAddresses( + addresses.contract, + addresses.user1 + ); +}); +await dispatchTx.prove(); +pendingTx = await dispatchTx.sign([senderKey]).send(); +await pendingTx.wait(); +toc('dispatch transactions'); + +tic('proving inclusion'); +// check if the actions contain the `sender` address +let containsTx = await Mina.transaction(senderSpec, async () => { + await merkleListReducerContract.assertContainsAddress(sender); + await merkleListReducerContract.assertContainsAddress(addresses.user1); + await merkleListReducerContract.assertContainsAddress(addresses.user2); + await merkleListReducerContract.assertContainsAddress(addresses.contract); +}); +await containsTx.prove(); +pendingTx = await containsTx.sign([senderKey]).send(); +await pendingTx.wait(); +toc('proving inclusion'); + +tic('dispatch transactions (multi-transaction)'); + +tic('building'); +const txs = []; +let dispatchTx1 = await Mina.transaction(senderSpec, async () => { + await merkleListReducerContract.postAddress(addresses.user1); +}); +await dispatchTx1.prove(); +txs.push({ tx: dispatchTx1, privateKey: senderKey }); + +let dispatchTx2 = await Mina.transaction(sender2Spec, async () => { + await merkleListReducerContract.postTwoAddresses( + addresses.user2, + addresses.contract + ); +}); +await dispatchTx2.prove(); +txs.push({ tx: dispatchTx2, privateKey: sender2Key }); +toc('building'); + +tic('sending'); +const txPromises = []; +for (let i = 0; i < txs.length; i++) { + txPromises.push(txs[i].tx.sign([txs[i].privateKey]).send()); +} +await Promise.all(txPromises); +toc('sending'); + +tic('waiting'); +await new Promise((_r) => setTimeout(_r, 20_000)); +toc('waiting'); + +toc('dispatch transactions (multi-transaction)'); + +tic('proving inclusion (multi-transaction)'); +// check if the actions contain the `sender` address +let containsTx2 = await Mina.transaction(senderSpec, async () => { + await merkleListReducerContract.assertContainsAddress(addresses.user1); + await merkleListReducerContract.assertContainsAddress(addresses.user2); + await merkleListReducerContract.assertContainsAddress(addresses.contract); +}); +await containsTx2.prove(); +pendingTx = await containsTx2.sign([senderKey]).send(); +await pendingTx.wait(); +toc('proving inclusion (multi-transaction)'); + +// ---- +toc(); diff --git a/src/examples/zkapps/reducer/run.ts b/src/examples/zkapps/reducer/run.ts new file mode 100644 index 0000000000..d029c6f9ae --- /dev/null +++ b/src/examples/zkapps/reducer/run.ts @@ -0,0 +1,5 @@ +import { testLocal as testActionsContract } from './actions-as-merkle-list-iterator.js'; +import { testLocal as testMerkleListContract } from './actions-as-merkle-list.js'; + +await testMerkleListContract(); +await testActionsContract(); diff --git a/src/lib/mina/fetch.ts b/src/lib/mina/fetch.ts index d3bcce732f..8165bad6f7 100644 --- a/src/lib/mina/fetch.ts +++ b/src/lib/mina/fetch.ts @@ -734,6 +734,7 @@ export function createActionsList( accountInfo: ActionsQueryInputs, fetchedActions: FetchedAction[] ) { + const _fetchedActions = fetchedActions; const { publicKey, actionStates } = accountInfo; let actionsList: { actions: string[][]; hash: string }[] = []; @@ -756,8 +757,9 @@ export function createActionsList( `No action data was found for the account ${publicKey} with the latest action state ${actionState}` ); - // Reverse order so that multiple actions in the same block get replayed in reverse - actionData = actionData.reverse(); + actionData = actionData.sort((a1, a2) => { + return Number(a1.accountUpdateId) < Number(a2.accountUpdateId) ? -1 : 1; + }); // split actions by account update let actionsByAccountUpdate: string[][][] = []; diff --git a/src/lib/mina/graphql.ts b/src/lib/mina/graphql.ts index fd39322898..04e0340a61 100644 --- a/src/lib/mina/graphql.ts +++ b/src/lib/mina/graphql.ts @@ -9,6 +9,7 @@ export { type GenesisConstantsResponse, type FailureReasonResponse, type LastBlockQueryFailureCheckResponse, + type FetchedAction, type FetchedBlock, type TransactionStatus, type TransactionStatusQueryResponse, @@ -250,7 +251,7 @@ type EventQueryResponse = { }[]; }; -export type FetchedAction = { +type FetchedAction = { blockInfo: { distanceFromMaxBlockHeight: number; }; diff --git a/src/lib/mina/mina.ts b/src/lib/mina/mina.ts index a30c290bc5..2dc41d5422 100644 --- a/src/lib/mina/mina.ts +++ b/src/lib/mina/mina.ts @@ -448,7 +448,7 @@ function Network( if (actions !== undefined) return actions; } throw Error( - `getActions: Could not find actions for the public key ${publicKey}` + `getActions: Could not find actions for the public key ${publicKey.toBase58()}` ); }, proofsEnabled: true, From 332f371df0caf6cf4dbbd1e07be9a57b739f5f46 Mon Sep 17 00:00:00 2001 From: Coby Date: Fri, 2 Aug 2024 14:03:25 -0400 Subject: [PATCH 24/49] fix typing of sender spec in test --- src/examples/zkapps/reducer/run-live.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/examples/zkapps/reducer/run-live.ts b/src/examples/zkapps/reducer/run-live.ts index 4cbfe77db4..46a37fa2c5 100644 --- a/src/examples/zkapps/reducer/run-live.ts +++ b/src/examples/zkapps/reducer/run-live.ts @@ -47,8 +47,8 @@ await MerkleListReducing.compile(); toc(); const merkleListReducerContract = new MerkleListReducing(addresses.contract); -let senderSpec = { sender, fee: 1000000000n }; -let sender2Spec = { sender: sender2, fee: 1000000000n }; +let senderSpec = { sender, fee: 1000000000 }; +let sender2Spec = { sender: sender2, fee: 1000000000 }; tic('deploy contracts'); let deployTx = await Mina.transaction(senderSpec, async () => { From b0a59da78c919e5dcb384ddc0d773b46607bf1db Mon Sep 17 00:00:00 2001 From: Coby Date: Fri, 2 Aug 2024 14:22:19 -0400 Subject: [PATCH 25/49] more CI fixes --- .../reducer/actions-as-merkle-list-iterator.ts | 3 --- .../zkapps/reducer/actions-as-merkle-list.ts | 3 --- src/examples/zkapps/reducer/run-live.ts | 16 ++++++++-------- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts b/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts index 0400fbd6b9..643842a640 100644 --- a/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts +++ b/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts @@ -125,7 +125,4 @@ async function testLocal() { await accTx.sign([sender.key]).send(); assert(contract.counter.get().toBigInt() === 55n); - - await new Promise((_r) => setTimeout(_r, 5000)); - Mina.setActiveInstance(Mina.default); } diff --git a/src/examples/zkapps/reducer/actions-as-merkle-list.ts b/src/examples/zkapps/reducer/actions-as-merkle-list.ts index 33e37fd9a3..42e2160f88 100644 --- a/src/examples/zkapps/reducer/actions-as-merkle-list.ts +++ b/src/examples/zkapps/reducer/actions-as-merkle-list.ts @@ -116,7 +116,4 @@ async function testLocal() { ); await containsTx.prove(); await containsTx.sign([sender.key]).send(); - - await new Promise((_r) => setTimeout(_r, 5000)); - Mina.setActiveInstance(Mina.default); } diff --git a/src/examples/zkapps/reducer/run-live.ts b/src/examples/zkapps/reducer/run-live.ts index 46a37fa2c5..caf33f52f6 100644 --- a/src/examples/zkapps/reducer/run-live.ts +++ b/src/examples/zkapps/reducer/run-live.ts @@ -60,7 +60,7 @@ let deployTx = await Mina.transaction(senderSpec, async () => { pendingTx = await deployTx.sign([senderKey, keys.contract]).send(); await pendingTx.wait(); -toc('deploy contracts'); +toc(); console.log('working so far'); // push some actions @@ -78,7 +78,7 @@ let dispatchTx = await Mina.transaction(senderSpec, async () => { await dispatchTx.prove(); pendingTx = await dispatchTx.sign([senderKey]).send(); await pendingTx.wait(); -toc('dispatch transactions'); +toc(); tic('proving inclusion'); // check if the actions contain the `sender` address @@ -91,7 +91,7 @@ let containsTx = await Mina.transaction(senderSpec, async () => { await containsTx.prove(); pendingTx = await containsTx.sign([senderKey]).send(); await pendingTx.wait(); -toc('proving inclusion'); +toc(); tic('dispatch transactions (multi-transaction)'); @@ -111,7 +111,7 @@ let dispatchTx2 = await Mina.transaction(sender2Spec, async () => { }); await dispatchTx2.prove(); txs.push({ tx: dispatchTx2, privateKey: sender2Key }); -toc('building'); +toc(); tic('sending'); const txPromises = []; @@ -119,13 +119,13 @@ for (let i = 0; i < txs.length; i++) { txPromises.push(txs[i].tx.sign([txs[i].privateKey]).send()); } await Promise.all(txPromises); -toc('sending'); +toc(); tic('waiting'); await new Promise((_r) => setTimeout(_r, 20_000)); -toc('waiting'); +toc(); -toc('dispatch transactions (multi-transaction)'); +toc(); tic('proving inclusion (multi-transaction)'); // check if the actions contain the `sender` address @@ -137,7 +137,7 @@ let containsTx2 = await Mina.transaction(senderSpec, async () => { await containsTx2.prove(); pendingTx = await containsTx2.sign([senderKey]).send(); await pendingTx.wait(); -toc('proving inclusion (multi-transaction)'); +toc(); // ---- toc(); From 6a004453e7ee07a780189ff5901faa66a58bed5b Mon Sep 17 00:00:00 2001 From: Coby Date: Fri, 2 Aug 2024 15:30:18 -0400 Subject: [PATCH 26/49] fix name typo in ci script --- run-ci-live-tests.sh | 4 ++-- src/bindings | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/run-ci-live-tests.sh b/run-ci-live-tests.sh index 1355b00208..d49f1dbc6a 100755 --- a/run-ci-live-tests.sh +++ b/run-ci-live-tests.sh @@ -15,14 +15,14 @@ echo "" ./run src/examples/zkapps/hello-world/run-live.ts --bundle | add_prefix "HELLO_WORLD" & HELLO_WORLD_PROC=$! +./run src/examples/zkapps/reducer/run-live.ts --bundle | add_prefix "REDUCER" & +REDUCER_FLOW_PROC=$! ./run src/examples/zkapps/dex/run-live.ts --bundle | add_prefix "DEX" & DEX_PROC=$! ./run src/examples/fetch-live.ts --bundle | add_prefix "FETCH" & FETCH_PROC=$! ./run src/tests/transaction-flow.ts --bundle | add_prefix "TRANSACTION_FLOW" & TRANSACTION_FLOW_PROC=$! -./run src/examples/reducer/run-live.ts --bundle | add_prefix "REDUCER" & -REDUCER_FLOW_PROC=$! # Wait for each process and capture their exit statuses FAILURE=0 diff --git a/src/bindings b/src/bindings index 71f2e698da..d779882a38 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 71f2e698dadcdfc62c76a72248c0df71cfd39d4c +Subproject commit d779882a38eac79703cc7215cf9bc5fc0cd5cd65 From aab8760f8aceb4c56af0d869b8f5953f4de740c5 Mon Sep 17 00:00:00 2001 From: Coby Date: Fri, 2 Aug 2024 16:33:24 -0400 Subject: [PATCH 27/49] release lightnet accounts upon completion --- src/examples/zkapps/reducer/run-live.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/examples/zkapps/reducer/run-live.ts b/src/examples/zkapps/reducer/run-live.ts index caf33f52f6..2d5707e0be 100644 --- a/src/examples/zkapps/reducer/run-live.ts +++ b/src/examples/zkapps/reducer/run-live.ts @@ -29,7 +29,7 @@ Mina.setActiveInstance(network); let { keys, addresses } = randomAccounts('contract', 'user1', 'user2'); -let tx, pendingTx: Mina.PendingTransaction, balances, oldBalances; +let pendingTx: Mina.PendingTransaction; // compile contracts & wait for fee payer to be funded const senderKey = useCustomLocalNetwork @@ -61,7 +61,6 @@ pendingTx = await deployTx.sign([senderKey, keys.contract]).send(); await pendingTx.wait(); toc(); -console.log('working so far'); // push some actions tic('dispatch transactions'); @@ -141,3 +140,16 @@ toc(); // ---- toc(); + +console.log('Success!'); + +// Tear down +const keyPairReleaseMessage = await Lightnet.releaseKeyPair({ + publicKey: sender.toBase58(), +}); +if (keyPairReleaseMessage) console.info(keyPairReleaseMessage); + +const keyPairReleaseMessage2 = await Lightnet.releaseKeyPair({ + publicKey: sender2.toBase58(), +}); +if (keyPairReleaseMessage2) console.info(keyPairReleaseMessage2); From 01cae167c2204923f64393e4bdfcf31828f2b0d7 Mon Sep 17 00:00:00 2001 From: Coby Date: Fri, 2 Aug 2024 17:14:41 -0400 Subject: [PATCH 28/49] await specif txs in ci --- src/examples/zkapps/reducer/run-live.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/examples/zkapps/reducer/run-live.ts b/src/examples/zkapps/reducer/run-live.ts index 2d5707e0be..fac06d77ac 100644 --- a/src/examples/zkapps/reducer/run-live.ts +++ b/src/examples/zkapps/reducer/run-live.ts @@ -117,11 +117,12 @@ const txPromises = []; for (let i = 0; i < txs.length; i++) { txPromises.push(txs[i].tx.sign([txs[i].privateKey]).send()); } -await Promise.all(txPromises); +await txPromises[0].wait(); +await txPromises[1].wait(); toc(); tic('waiting'); -await new Promise((_r) => setTimeout(_r, 20_000)); +await new Promise((_r) => setTimeout(_r, 60_000)); toc(); toc(); From 7b6be89c59e23aedddde439f9800bb0a6bef81aa Mon Sep 17 00:00:00 2001 From: ymekuria Date: Wed, 14 Aug 2024 11:47:48 -0400 Subject: [PATCH 29/49] chore(bindings): update subproject commit reference to 9c3001268 for consistency --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 1f9522301a..9c30012688 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 1f9522301a8f6ec795e2214a3b4f9107005e2910 +Subproject commit 9c3001268881c5ff185bbc489bbd42f7341ea8f8 From a6510ff61f63f4c7335288370533eeb976694a9f Mon Sep 17 00:00:00 2001 From: ymekuria Date: Wed, 14 Aug 2024 11:58:37 -0400 Subject: [PATCH 30/49] chore(mina): update subproject commit reference in mina submodule --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index fe17617563..02accb5bc5 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit fe17617563897f041a73530ad98a118b37eec665 +Subproject commit 02accb5bc5bd152b5b849d864b3339ada56ef891 From f476b3a63cd5f63352034085ab670608b95f6b63 Mon Sep 17 00:00:00 2001 From: Coby Date: Wed, 14 Aug 2024 13:40:01 -0400 Subject: [PATCH 31/49] PR comments / cleanup --- src/examples/utils/network-configuration.ts | 9 ++++++ .../{randomAccounts.ts => random-accounts.ts} | 8 +++-- .../actions-as-merkle-list-iterator.ts | 2 +- .../zkapps/reducer/actions-as-merkle-list.ts | 2 +- src/examples/zkapps/reducer/run-live.ts | 29 ++++--------------- src/lib/mina/fetch.unit-test.ts | 2 +- ...sResponse.ts => fetch-actions-response.ts} | 6 +++- 7 files changed, 29 insertions(+), 29 deletions(-) create mode 100644 src/examples/utils/network-configuration.ts rename src/examples/utils/{randomAccounts.ts => random-accounts.ts} (83%) rename src/lib/mina/fixtures/{fetchActionsResponse.ts => fetch-actions-response.ts} (98%) diff --git a/src/examples/utils/network-configuration.ts b/src/examples/utils/network-configuration.ts new file mode 100644 index 0000000000..1fb26628a9 --- /dev/null +++ b/src/examples/utils/network-configuration.ts @@ -0,0 +1,9 @@ +export { + DEFAULT_LIGHTNET_CONFIG +} + +const DEFAULT_LIGHTNET_CONFIG = { + mina: 'http://localhost:8080/graphql', + archive: 'http://localhost:8282', + lightnetAccountManager: 'http://localhost:8181', +} \ No newline at end of file diff --git a/src/examples/utils/randomAccounts.ts b/src/examples/utils/random-accounts.ts similarity index 83% rename from src/examples/utils/randomAccounts.ts rename to src/examples/utils/random-accounts.ts index 2b43a511d6..c1ca28265e 100644 --- a/src/examples/utils/randomAccounts.ts +++ b/src/examples/utils/random-accounts.ts @@ -1,9 +1,13 @@ -import { PrivateKey, PublicKey } from '../../../dist/node/index.js'; +import { PrivateKey, PublicKey } from 'o1js'; + +export { + randomAccounts +} /** * Predefined accounts keys, labeled by the input strings. Useful for testing/debugging with consistent keys. */ -export function randomAccounts( +function randomAccounts( ...names: [K, ...K[]] ): { keys: Record; addresses: Record } { let base58Keys = Array(names.length) diff --git a/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts b/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts index 643842a640..15b9c6e673 100644 --- a/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts +++ b/src/examples/zkapps/reducer/actions-as-merkle-list-iterator.ts @@ -18,7 +18,7 @@ import { SmartContract, method, assert, -} from '../../../../dist/node/index.js'; +} from 'o1js'; export { ActionsContract, testLocal }; diff --git a/src/examples/zkapps/reducer/actions-as-merkle-list.ts b/src/examples/zkapps/reducer/actions-as-merkle-list.ts index 42e2160f88..4d66e92b48 100644 --- a/src/examples/zkapps/reducer/actions-as-merkle-list.ts +++ b/src/examples/zkapps/reducer/actions-as-merkle-list.ts @@ -17,7 +17,7 @@ import { SmartContract, method, assert, -} from '../../../../dist/node/index.js'; +} from 'o1js'; export { MerkleListReducing, testLocal }; diff --git a/src/examples/zkapps/reducer/run-live.ts b/src/examples/zkapps/reducer/run-live.ts index fac06d77ac..dc291dba01 100644 --- a/src/examples/zkapps/reducer/run-live.ts +++ b/src/examples/zkapps/reducer/run-live.ts @@ -3,28 +3,15 @@ import { Lightnet, Mina, PrivateKey, -} from '../../../../dist/node/index.js'; -import { randomAccounts } from '../../utils/randomAccounts.js'; +} from 'o1js'; +import { DEFAULT_LIGHTNET_CONFIG } from '../../utils/network-configuration.js'; +import { randomAccounts } from '../../utils/random-accounts.js'; import { tic, toc } from '../../utils/tic-toc.node.js'; import { MerkleListReducing } from './actions-as-merkle-list.js'; -const useCustomLocalNetwork = process.env.USE_CUSTOM_LOCAL_NETWORK === 'true'; - -if (!useCustomLocalNetwork) { - throw 'Only Lightnet is currently supported'; -} - tic('Run reducer examples against real network.'); console.log(); -const network = Mina.Network({ - mina: useCustomLocalNetwork - ? 'http://localhost:8080/graphql' - : 'https://berkeley.minascan.io/graphql', - archive: useCustomLocalNetwork - ? 'http://localhost:8282' - : 'https://api.minascan.io/archive/berkeley/v1/graphql', - lightnetAccountManager: 'http://localhost:8181', -}); +const network = Mina.Network(DEFAULT_LIGHTNET_CONFIG); Mina.setActiveInstance(network); let { keys, addresses } = randomAccounts('contract', 'user1', 'user2'); @@ -32,14 +19,10 @@ let { keys, addresses } = randomAccounts('contract', 'user1', 'user2'); let pendingTx: Mina.PendingTransaction; // compile contracts & wait for fee payer to be funded -const senderKey = useCustomLocalNetwork - ? (await Lightnet.acquireKeyPair()).privateKey - : PrivateKey.random(); +const senderKey = (await Lightnet.acquireKeyPair()).privateKey const sender = senderKey.toPublicKey(); -const sender2Key = useCustomLocalNetwork - ? (await Lightnet.acquireKeyPair()).privateKey - : PrivateKey.random(); +const sender2Key = (await Lightnet.acquireKeyPair()).privateKey const sender2 = sender2Key.toPublicKey(); tic('Compiling Merkle List Reducer Smart Contract'); diff --git a/src/lib/mina/fetch.unit-test.ts b/src/lib/mina/fetch.unit-test.ts index 5678eee9d6..3f0d662cec 100644 --- a/src/lib/mina/fetch.unit-test.ts +++ b/src/lib/mina/fetch.unit-test.ts @@ -1,6 +1,6 @@ import { PrivateKey, TokenId } from 'o1js'; import { createActionsList } from './fetch.js'; -import { mockFetchActionsResponse } from './fixtures/fetchActionsResponse.js'; +import { mockFetchActionsResponse } from './fixtures/fetch-actions-response.js'; import { test, describe } from 'node:test'; import { removeJsonQuotes } from './graphql.js'; import { expect } from 'expect'; diff --git a/src/lib/mina/fixtures/fetchActionsResponse.ts b/src/lib/mina/fixtures/fetch-actions-response.ts similarity index 98% rename from src/lib/mina/fixtures/fetchActionsResponse.ts rename to src/lib/mina/fixtures/fetch-actions-response.ts index 20e5648b87..3e280bbff2 100644 --- a/src/lib/mina/fixtures/fetchActionsResponse.ts +++ b/src/lib/mina/fixtures/fetch-actions-response.ts @@ -1,4 +1,8 @@ -export const mockFetchActionsResponse = +export { + mockFetchActionsResponse +} + +const mockFetchActionsResponse = { "data": { "actions": [ From 599fe060ccab7d8a30f826e361c50c0adb2f4d16 Mon Sep 17 00:00:00 2001 From: Coby Date: Wed, 14 Aug 2024 15:18:28 -0400 Subject: [PATCH 32/49] update bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index d779882a38..71f2e698da 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit d779882a38eac79703cc7215cf9bc5fc0cd5cd65 +Subproject commit 71f2e698dadcdfc62c76a72248c0df71cfd39d4c From 0feea171e330050bc7592ce0236962e129e30701 Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 19 Aug 2024 16:28:53 +0200 Subject: [PATCH 33/49] update submodules --- src/bindings | 2 +- src/mina | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bindings b/src/bindings index 71f2e698da..667b9dda13 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 71f2e698dadcdfc62c76a72248c0df71cfd39d4c +Subproject commit 667b9dda130587800b88144b238da3a1a77853c4 diff --git a/src/mina b/src/mina index 02accb5bc5..24c8b2d723 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 02accb5bc5bd152b5b849d864b3339ada56ef891 +Subproject commit 24c8b2d723fb09d0d7f996b6ac35373dc27084ef From 5adc7529f488ed009fb8081f62aa6ba839ebe3b9 Mon Sep 17 00:00:00 2001 From: ymekuria Date: Tue, 20 Aug 2024 13:44:30 -0700 Subject: [PATCH 34/49] feat(dex.ts): update UInt64x2 Struct layout --- src/examples/zkapps/dex/dex.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index 3292ee81d4..32bc3de36b 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -20,7 +20,7 @@ import { export { TokenContract, addresses, createDex, keys, randomAccounts, tokenIds }; -class UInt64x2 extends Struct([UInt64, UInt64]) {} +class UInt64x2 extends Struct({ values: [UInt64, UInt64] }) {} function createDex({ lockedLiquiditySlots, From fa67c3f416a10018456a6129640cd08c24631681 Mon Sep 17 00:00:00 2001 From: ymekuria Date: Tue, 20 Aug 2024 13:47:51 -0700 Subject: [PATCH 35/49] feat(dex.ts): update `redeemLiquidityPartialchange` method to return new UInt64x2 layout --- src/examples/zkapps/dex/dex.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index 32bc3de36b..d9aff79eda 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -264,7 +264,7 @@ function createDex({ this.self.body.mayUseToken = AccountUpdate.MayUseToken.ParentsOwnToken; // return l, dy so callers don't have to walk their child account updates to get it - return [l, dy]; + return { values: [l, dy] }; } // more complicated circuit, where we trigger the Y(other)-lqXY trade in our child account updates and then add the X(our) part From cd55a51fcc20085928f84522de3f3da754a5e16c Mon Sep 17 00:00:00 2001 From: ymekuria Date: Tue, 20 Aug 2024 15:24:08 -0700 Subject: [PATCH 36/49] feat(dex.ts): update `redeemLiquidity` method to return new UInt64x2 layout --- src/examples/zkapps/dex/dex.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index d9aff79eda..c91eff0a0d 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -289,7 +289,7 @@ function createDex({ // just subtract the balance, user gets their part one level higher this.balance.subInPlace(dx); - return [dx, dy]; + return { values: [dx, dy] }; } // this works for both directions (in our case where both tokens use the same contract) From 55fe21b3f8bf8a6c94c167319143976318512b48 Mon Sep 17 00:00:00 2001 From: ymekuria Date: Tue, 20 Aug 2024 15:57:56 -0700 Subject: [PATCH 37/49] refactor(dex.ts): destructure values from the result of redeemLiquidityPartial function for better readability and maintainability --- src/examples/zkapps/dex/dex.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index c91eff0a0d..413c4ded23 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -277,9 +277,9 @@ function createDex({ // first call the Y token holder, approved by the Y token contract; this makes sure we get dl, the user's lqXY let tokenY = new TokenContract(otherTokenAddress); let dexY = new DexTokenHolder(this.address, tokenY.deriveTokenId()); - let result = await dexY.redeemLiquidityPartial(user, dl); - let l = result[0]; - let dy = result[1]; + let { values } = await dexY.redeemLiquidityPartial(user, dl); + let l = values[0]; + let dy = values[1]; await tokenY.transfer(dexY.self, user, dy); // in return for dl, we give back dx, the X token part From 8821411543eb4bb9e3c1627ef1748effbb265d0e Mon Sep 17 00:00:00 2001 From: ymekuria Date: Tue, 20 Aug 2024 16:05:09 -0700 Subject: [PATCH 38/49] refactor(dex.ts): destructure the return value of dexX.redeemLiquidity() to fit new layout structure --- src/examples/zkapps/dex/dex.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index 413c4ded23..befa493ed0 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -158,7 +158,11 @@ function createDex({ let sender = this.sender.getUnconstrained(); // unconstrained because redeemLiquidity() requires the signature anyway let tokenX = new TokenContract(this.tokenX); let dexX = new DexTokenHolder(this.address, tokenX.deriveTokenId()); - let dxdy = await dexX.redeemLiquidity(sender, dl, this.tokenY); + let { values: dxdy } = await dexX.redeemLiquidity( + sender, + dl, + this.tokenY + ); let dx = dxdy[0]; await tokenX.transfer(dexX.self, sender, dx); return dxdy; From 78175fe624aaf1169123a89340aa458f3d7259cd Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 21 Aug 2024 14:16:12 +0200 Subject: [PATCH 39/49] fix int64 mod when the input is negative and result = 0 --- src/lib/provable/int.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/provable/int.ts b/src/lib/provable/int.ts index bc4e741f91..e919b17a9e 100644 --- a/src/lib/provable/int.ts +++ b/src/lib/provable/int.ts @@ -1334,7 +1334,11 @@ class Int64 extends CircuitValue implements BalanceChange { let isNonNegative = this.magnitude .equals(UInt64.zero) .or(this.sgn.isPositive()); - rest = Provable.if(isNonNegative, rest, y_.value.sub(rest)); + rest = Provable.if( + isNonNegative.or(rest.equals(0)), + rest, + y_.value.sub(rest) + ); return new Int64(new UInt64(rest.value)); } From ac03ac549188a64f4708074adf30b4db1efbf0f0 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 21 Aug 2024 14:18:06 +0200 Subject: [PATCH 40/49] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index baf5beaf6a..a6b14ffc8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/d6abf1d97...HEAD) +### Fixes + +- Fix behavior of `Int64.modV2()` when the input is negative and the remainder should be 0 https://github.com/o1-labs/o1js/pull/1797 + ## [1.6.0](https://github.com/o1-labs/o1js/compare/1ad7333e9e...d6abf1d97) - 2024-07-23 ### Added From ecac253a4002f51a107552fcefc5b2f2aec9dae4 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 21 Aug 2024 14:32:31 +0200 Subject: [PATCH 41/49] int64 to bigint --- src/lib/provable/int.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/lib/provable/int.ts b/src/lib/provable/int.ts index e919b17a9e..15c64c6249 100644 --- a/src/lib/provable/int.ts +++ b/src/lib/provable/int.ts @@ -1147,14 +1147,22 @@ class Int64 extends CircuitValue implements BalanceChange { return Int64.create(UInt64.from(obj.magnitude), Sign.fromValue(obj.sgn)); } + /** + * Turns the {@link Int64} into a {@link BigInt}. + */ + toBigint() { + let abs = this.magnitude.toBigInt(); + let sgn = this.sgn.isPositive().toBoolean() ? 1n : -1n; + return sgn * abs; + } + /** * Turns the {@link Int64} into a string. */ toString() { - let abs = this.magnitude.toString(); - let sgn = this.isPositive().toBoolean() || abs === '0' ? '' : '-'; - return sgn + abs; + return this.toBigint().toString(); } + isConstant() { return this.magnitude.value.isConstant() && this.sgn.isConstant(); } From a0d2f4b86c3b52f39856cd9888b07c34309d645e Mon Sep 17 00:00:00 2001 From: ymekuria Date: Wed, 21 Aug 2024 08:25:31 -0700 Subject: [PATCH 42/49] chore(bindings): update subproject commit reference to 35b2a6135cdc0ed1fda6fc0262eedea9034fa948 --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 9c30012688..35b2a6135c 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 9c3001268881c5ff185bbc489bbd42f7341ea8f8 +Subproject commit 35b2a6135cdc0ed1fda6fc0262eedea9034fa948 From def8e4f9cc9f5a3dc7ff7faa7603e8a5e535aab9 Mon Sep 17 00:00:00 2001 From: ymekuria Date: Wed, 21 Aug 2024 08:39:51 -0700 Subject: [PATCH 43/49] chore(mina): update subproject commit reference to 02accb5bc5bd152b5b849d864b3339ada56ef891 --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index 24c8b2d723..02accb5bc5 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 24c8b2d723fb09d0d7f996b6ac35373dc27084ef +Subproject commit 02accb5bc5bd152b5b849d864b3339ada56ef891 From fa8462fe64437527fe16db368b919831eeed3b73 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 22 Aug 2024 15:52:20 +0200 Subject: [PATCH 44/49] fix this.sender vulnerability --- src/lib/mina/zkapp.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/lib/mina/zkapp.ts b/src/lib/mina/zkapp.ts index 7b58bfc361..30984a0bec 100644 --- a/src/lib/mina/zkapp.ts +++ b/src/lib/mina/zkapp.ts @@ -881,6 +881,8 @@ super.init(); * * **Warning**: The fact that this public key equals the current sender is not part of the proof. * A malicious prover could use any other public key without affecting the validity of the proof. + * + * Consider using `this.sender.getAndRequireSignatureV2()` if you need to prove that the sender controls this account. */ getUnconstrained(): PublicKey { // TODO this logic now has some overlap with this.self, we should combine them somehow @@ -900,11 +902,31 @@ super.init(); } }, + /** + * @deprecated + * Deprecated in favor of `this.sender.getAndRequireSignatureV2()`. + * This method is vulnerable because it allows the prover to return a dummy (empty) public key. + */ getAndRequireSignature(): PublicKey { let sender = this.getUnconstrained(); AccountUpdate.createSigned(sender); return sender; }, + + /** + * Return a public key that is forced to sign this transaction. + * + * Note: This doesn't prove that the return value is the transaction sender, but it does prove that whoever created + * the transaction controls the private key associated with the returned public key. + */ + getAndRequireSignatureV2(): PublicKey { + let sender = this.getUnconstrained(); + // we prove that the returned public key is not the empty key, in which case `createSigned()` would + // skip adding the account update + sender.x.assertNotEquals(0); + AccountUpdate.createSigned(sender); + return sender; + }, }; /** From 21e4940fd98299a9b7d038bb97cb8336c0163363 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 22 Aug 2024 15:53:46 +0200 Subject: [PATCH 45/49] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6b14ffc8a..86a4b871f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/d6abf1d97...HEAD) +### Deprecated + +- `this.sender.getAndRequireSignature()` deprecated in favor of `getAndRequireSignatureV2()` due to a vulnerability https://github.com/o1-labs/o1js/pull/1799 + ### Fixes - Fix behavior of `Int64.modV2()` when the input is negative and the remainder should be 0 https://github.com/o1-labs/o1js/pull/1797 From a8df62c57bb0d59b1a685ad61c59f4d3faae82fd Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 22 Aug 2024 16:47:54 +0200 Subject: [PATCH 46/49] also deprecate getUnconstrained --- CHANGELOG.md | 2 +- src/lib/mina/zkapp.ts | 37 ++++++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86a4b871f3..5390d99522 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Deprecated -- `this.sender.getAndRequireSignature()` deprecated in favor of `getAndRequireSignatureV2()` due to a vulnerability https://github.com/o1-labs/o1js/pull/1799 +- `this.sender.getAndRequireSignature()` / `getUnconstrained()` deprecated in favor of `V2` versions due to a vulnerability https://github.com/o1-labs/o1js/pull/1799 ### Fixes diff --git a/src/lib/mina/zkapp.ts b/src/lib/mina/zkapp.ts index 30984a0bec..43a634cb91 100644 --- a/src/lib/mina/zkapp.ts +++ b/src/lib/mina/zkapp.ts @@ -875,14 +875,10 @@ super.init(); sender = { self: this as SmartContract, /** - * The public key of the current transaction's sender account. - * - * Throws an error if not inside a transaction, or the sender wasn't passed in. - * - * **Warning**: The fact that this public key equals the current sender is not part of the proof. - * A malicious prover could use any other public key without affecting the validity of the proof. - * - * Consider using `this.sender.getAndRequireSignatureV2()` if you need to prove that the sender controls this account. + * @deprecated + * Deprecated in favor of `this.sender.getUnconstrainedV2()`. + * This method is vulnerable because it allows the prover to return a dummy (empty) public key, + * which would cause an account update with that public key to not be included. */ getUnconstrained(): PublicKey { // TODO this logic now has some overlap with this.self, we should combine them somehow @@ -902,6 +898,24 @@ super.init(); } }, + /** + * The public key of the current transaction's sender account. + * + * Throws an error if not inside a transaction, or the sender wasn't passed in. + * + * **Warning**: The fact that this public key equals the current sender is not part of the proof. + * A malicious prover could use any other public key without affecting the validity of the proof. + * + * Consider using `this.sender.getAndRequireSignatureV2()` if you need to prove that the sender controls this account. + */ + getUnconstrainedV2(): PublicKey { + let sender = this.getUnconstrained(); + // we prove that the returned public key is not the empty key, in which case + // `createSigned()` would skip adding the account update, and nothing is proved + sender.x.assertNotEquals(0); + return sender; + }, + /** * @deprecated * Deprecated in favor of `this.sender.getAndRequireSignatureV2()`. @@ -916,14 +930,11 @@ super.init(); /** * Return a public key that is forced to sign this transaction. * - * Note: This doesn't prove that the return value is the transaction sender, but it does prove that whoever created + * Note: This doesn't prove that the return value is the transaction sender, but it proves that whoever created * the transaction controls the private key associated with the returned public key. */ getAndRequireSignatureV2(): PublicKey { - let sender = this.getUnconstrained(); - // we prove that the returned public key is not the empty key, in which case `createSigned()` would - // skip adding the account update - sender.x.assertNotEquals(0); + let sender = this.getUnconstrainedV2(); AccountUpdate.createSigned(sender); return sender; }, From b733460176d5cd2304e4c48bcbfae209ee2a17c2 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 22 Aug 2024 16:50:21 +0200 Subject: [PATCH 47/49] upgrade examples --- src/examples/zkapps/dex/dex-with-actions.ts | 18 +++++++++--------- src/examples/zkapps/dex/dex.ts | 10 +++++----- src/examples/zkapps/escrow/token-escrow.ts | 2 +- src/examples/zkapps/simple-zkapp-payment.ts | 4 ++-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/examples/zkapps/dex/dex-with-actions.ts b/src/examples/zkapps/dex/dex-with-actions.ts index bb0fe5583f..87456419cf 100644 --- a/src/examples/zkapps/dex/dex-with-actions.ts +++ b/src/examples/zkapps/dex/dex-with-actions.ts @@ -2,11 +2,11 @@ * This DEX implementation differs from ./dex.ts in two ways: * - More minimal & realistic; stuff designed only for testing protocol features was removed * - Uses an async pattern with actions that lets users claim funds later and reduces account updates - * - * Warning: The reducer API in o1js is currently not safe to use in production applications. The `reduce()` - * method breaks if more than the hard-coded number (default: 32) of actions are pending. Work is actively + * + * Warning: The reducer API in o1js is currently not safe to use in production applications. The `reduce()` + * method breaks if more than the hard-coded number (default: 32) of actions are pending. Work is actively * in progress to mitigate this limitation. - */ + */ import { Account, AccountUpdate, @@ -86,7 +86,7 @@ class Dex extends TokenContract { @method async createAccount() { this.internal.mint({ // unconstrained because we don't care which account is created - address: this.sender.getUnconstrained(), + address: this.sender.getUnconstrainedV2(), amount: UInt64.from(0), }); } @@ -104,7 +104,7 @@ class Dex extends TokenContract { @method.returns(UInt64) async supplyLiquidityBase(dx: UInt64, dy: UInt64) { // unconstrained because `transfer()` requires sender signature anyway - let user = this.sender.getUnconstrained(); + let user = this.sender.getUnconstrainedV2(); let tokenX = new TrivialCoin(this.tokenX); let tokenY = new TrivialCoin(this.tokenY); @@ -175,7 +175,7 @@ class Dex extends TokenContract { * contracts pay you tokens when reducing the action. */ @method async redeemInitialize(dl: UInt64) { - let sender = this.sender.getUnconstrained(); // unconstrained because `burn()` requires sender signature anyway + let sender = this.sender.getUnconstrainedV2(); // unconstrained because `burn()` requires sender signature anyway this.reducer.dispatch(new RedeemAction({ address: sender, dl })); this.internal.burn({ address: sender, amount: dl }); // TODO: preconditioning on the state here ruins concurrent interactions, @@ -209,7 +209,7 @@ class Dex extends TokenContract { * the called methods which requires proof authorization. */ async swapX(dx: UInt64) { - let user = this.sender.getUnconstrained(); // unconstrained because `swap()` requires sender signature anyway + let user = this.sender.getUnconstrainedV2(); // unconstrained because `swap()` requires sender signature anyway let tokenY = new TrivialCoin(this.tokenY); let dexY = new DexTokenHolder(this.address, tokenY.deriveTokenId()); let dy = await dexY.swap(user, dx, this.tokenX); @@ -228,7 +228,7 @@ class Dex extends TokenContract { * the called methods which requires proof authorization. */ async swapY(dy: UInt64) { - let user = this.sender.getUnconstrained(); // unconstrained because `swap()` requires sender signature anyway + let user = this.sender.getUnconstrainedV2(); // unconstrained because `swap()` requires sender signature anyway let tokenX = new TrivialCoin(this.tokenX); let dexX = new DexTokenHolder(this.address, tokenX.deriveTokenId()); let dx = await dexX.swap(user, dy, this.tokenY); diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index befa493ed0..7d61d842ee 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -56,7 +56,7 @@ function createDex({ */ @method.returns(UInt64) async supplyLiquidityBase(dx: UInt64, dy: UInt64) { - let user = this.sender.getUnconstrained(); // unconstrained because transfer() requires the signature anyway + let user = this.sender.getUnconstrainedV2(); // unconstrained because transfer() requires the signature anyway let tokenX = new TokenContract(this.tokenX); let tokenY = new TokenContract(this.tokenY); @@ -155,7 +155,7 @@ function createDex({ */ async redeemLiquidity(dl: UInt64) { // call the token X holder inside a token X-approved callback - let sender = this.sender.getUnconstrained(); // unconstrained because redeemLiquidity() requires the signature anyway + let sender = this.sender.getUnconstrainedV2(); // unconstrained because redeemLiquidity() requires the signature anyway let tokenX = new TokenContract(this.tokenX); let dexX = new DexTokenHolder(this.address, tokenX.deriveTokenId()); let { values: dxdy } = await dexX.redeemLiquidity( @@ -177,7 +177,7 @@ function createDex({ */ @method.returns(UInt64) async swapX(dx: UInt64) { - let sender = this.sender.getUnconstrained(); // unconstrained because swap() requires the signature anyway + let sender = this.sender.getUnconstrainedV2(); // unconstrained because swap() requires the signature anyway let tokenY = new TokenContract(this.tokenY); let dexY = new DexTokenHolder(this.address, tokenY.deriveTokenId()); let dy = await dexY.swap(sender, dx, this.tokenX); @@ -194,7 +194,7 @@ function createDex({ */ @method.returns(UInt64) async swapY(dy: UInt64) { - let sender = this.sender.getUnconstrained(); // unconstrained because swap() requires the signature anyway + let sender = this.sender.getUnconstrainedV2(); // unconstrained because swap() requires the signature anyway let tokenX = new TokenContract(this.tokenX); let dexX = new DexTokenHolder(this.address, tokenX.deriveTokenId()); let dx = await dexX.swap(sender, dy, this.tokenY); @@ -233,7 +233,7 @@ function createDex({ @method.returns(UInt64) async swapX(dx: UInt64) { - let sender = this.sender.getUnconstrained(); // unconstrained because swap() requires the signature anyway + let sender = this.sender.getUnconstrainedV2(); // unconstrained because swap() requires the signature anyway let tokenY = new TokenContract(this.tokenY); let dexY = new ModifiedDexTokenHolder( this.address, diff --git a/src/examples/zkapps/escrow/token-escrow.ts b/src/examples/zkapps/escrow/token-escrow.ts index 39f01a9917..7a1bb49d79 100644 --- a/src/examples/zkapps/escrow/token-escrow.ts +++ b/src/examples/zkapps/escrow/token-escrow.ts @@ -29,7 +29,7 @@ class TokenEscrow extends SmartContract { */ @method async withdraw(amount: UInt64) { // only the admin can withdraw - this.sender.getAndRequireSignature().assertEquals(admin); + this.sender.getAndRequireSignatureV2().assertEquals(admin); // withdraw the amount let receiverAU = this.send({ to: admin, amount }); diff --git a/src/examples/zkapps/simple-zkapp-payment.ts b/src/examples/zkapps/simple-zkapp-payment.ts index 0563193679..eb58b51daa 100644 --- a/src/examples/zkapps/simple-zkapp-payment.ts +++ b/src/examples/zkapps/simple-zkapp-payment.ts @@ -18,12 +18,12 @@ class PaymentContainer extends SmartContract { @method async withdraw(amount: UInt64) { // unconstrained because we don't care where the user wants to withdraw to - let to = this.sender.getUnconstrained(); + let to = this.sender.getUnconstrainedV2(); this.send({ to, amount }); } @method async deposit(amount: UInt64) { - let sender = this.sender.getUnconstrained(); // unconstrained because we're already requiring a signature in the next line + let sender = this.sender.getUnconstrainedV2(); // unconstrained because we're already requiring a signature in the next line let senderUpdate = AccountUpdate.createSigned(sender); senderUpdate.send({ to: this, amount }); } From bb1577f66f1e55b247409a41830f9ea8d0029af3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 22 Aug 2024 16:53:37 +0200 Subject: [PATCH 48/49] missed two --- src/lib/mina/account-update-layout.unit-test.ts | 4 ++-- src/lib/mina/actions/batch-reducer.unit-test.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/mina/account-update-layout.unit-test.ts b/src/lib/mina/account-update-layout.unit-test.ts index 3c6fcc237a..8c05c49bc9 100644 --- a/src/lib/mina/account-update-layout.unit-test.ts +++ b/src/lib/mina/account-update-layout.unit-test.ts @@ -7,13 +7,13 @@ import { SmartContract, method } from './zkapp.js'; class NestedCall extends SmartContract { @method async deposit() { - let sender = this.sender.getUnconstrained(); + let sender = this.sender.getUnconstrainedV2(); let payerUpdate = AccountUpdate.createSigned(sender); payerUpdate.send({ to: this.address, amount: UInt64.one }); } @method async depositUsingTree() { - let sender = this.sender.getUnconstrained(); + let sender = this.sender.getUnconstrainedV2(); let payerUpdate = AccountUpdate.createSigned(sender); let receiverUpdate = AccountUpdate.create(this.address); payerUpdate.send({ to: receiverUpdate, amount: UInt64.one }); diff --git a/src/lib/mina/actions/batch-reducer.unit-test.ts b/src/lib/mina/actions/batch-reducer.unit-test.ts index ff06ffd90f..6c0d5a18d4 100644 --- a/src/lib/mina/actions/batch-reducer.unit-test.ts +++ b/src/lib/mina/actions/batch-reducer.unit-test.ts @@ -72,7 +72,7 @@ class UnsafeAirdrop extends SmartContract { */ @method async claim() { - let address = this.sender.getUnconstrained(); + let address = this.sender.getUnconstrainedV2(); // ensure that the MINA account already exists and that the sender knows its private key let au = AccountUpdate.createSigned(address); From 842947458499db8edaabd7df33750d846dcc0b7e Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 22 Aug 2024 20:42:37 +0200 Subject: [PATCH 49/49] dump vks --- tests/vk-regression/vk-regression.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index 6dbad49ac5..43b721bea0 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -84,7 +84,7 @@ } }, "Dex": { - "digest": "1ed56246c01d984270b8baccc1e94ba9c1dcc2852fa12323fa09a4ea9b8e243a", + "digest": "36ddefae20a75dfd0251919bc5e8484ae2bea6c699223601e04f505f3cc773c3", "methods": { "approveBase": { "rows": 13232, @@ -92,15 +92,15 @@ }, "supplyLiquidityBase": { "rows": 2841, - "digest": "26f29fba6f5bb72a58ff7bd5f1f38a5c" + "digest": "12bff149f793b19e76c40984b0b281c2" }, "swapX": { "rows": 1512, - "digest": "c9cc2dd606b458fcab809c7fbf9d5b07" + "digest": "aa3af00ecc1fcc3f36c0bf914fb4aadf" }, "swapY": { "rows": 1512, - "digest": "6c43e323161d2f005183cb2319bdfd52" + "digest": "8acd763ff839161dca0eb1cd7adfa0f8" }, "burnLiquidity": { "rows": 704, @@ -108,8 +108,8 @@ } }, "verificationKey": { - "data": "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", - "hash": "11995787401471485847112327267221844756507727092913123363897384059516663907637" + "data": "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", + "hash": "420941326129710742530246108547604999064741690698290539965276625281726897461" } }, "Group Primitive": {