From 221057bb677593ce054bde6acedfe55e6dce7287 Mon Sep 17 00:00:00 2001 From: Saurabh Patil <55076843+Saurabhpatil-dev@users.noreply.github.com> Date: Thu, 23 Nov 2023 00:38:53 +0530 Subject: [PATCH 1/3] :hammer: Update precondition apis to use require instead of assert as verb. Added test cases for verification --- src/lib/precondition.test.ts | 127 +++++++++++++++++++++++++++++++++++ src/lib/precondition.ts | 76 +++++++++++++++++---- 2 files changed, 190 insertions(+), 13 deletions(-) diff --git a/src/lib/precondition.test.ts b/src/lib/precondition.test.ts index d064ddc89f..07d0243d68 100644 --- a/src/lib/precondition.test.ts +++ b/src/lib/precondition.test.ts @@ -76,6 +76,21 @@ describe('preconditions', () => { expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); }); + it('get + requireEquals should not throw', async () => { + let nonce = zkapp.account.nonce.get(); + let tx = await Mina.transaction(feePayer, () => { + zkapp.requireSignature(); + for (let precondition of implemented) { + let p = precondition().get(); + precondition().requireEquals(p as any); + } + AccountUpdate.attachToTransaction(zkapp.self); + }); + await tx.sign([feePayerKey, zkappKey]).send(); + // check that tx was applied, by checking nonce was incremented + expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); + }); + it('get + assertEquals should throw for unimplemented fields', async () => { for (let precondition of unimplemented) { await expect( @@ -88,6 +103,18 @@ describe('preconditions', () => { } }); + it('get + requireEquals should throw for unimplemented fields', async () => { + for (let precondition of unimplemented) { + await expect( + Mina.transaction(feePayer, () => { + let p = precondition(); + p.requireEquals(p.get() as any); + AccountUpdate.attachToTransaction(zkapp.self); + }) + ).rejects.toThrow(/not implemented/); + } + }); + it('get + assertBetween should not throw', async () => { let nonce = zkapp.account.nonce.get(); let tx = await Mina.transaction(feePayer, () => { @@ -103,6 +130,21 @@ describe('preconditions', () => { expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); }); + it('get + requireBetween should not throw', async () => { + let nonce = zkapp.account.nonce.get(); + let tx = await Mina.transaction(feePayer, () => { + for (let precondition of implementedWithRange) { + let p: any = precondition().get(); + precondition().requireBetween(p.constructor.zero, p); + } + zkapp.requireSignature(); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await tx.sign([feePayerKey, zkappKey]).send(); + // check that tx was applied, by checking nonce was incremented + expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); + }); + it('satisfied currentSlot.assertBetween should not throw', async () => { let nonce = zkapp.account.nonce.get(); let tx = await Mina.transaction(feePayer, () => { @@ -117,6 +159,20 @@ describe('preconditions', () => { expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); }); + it('satisfied currentSlot.requireBetween should not throw', async () => { + let nonce = zkapp.account.nonce.get(); + let tx = await Mina.transaction(feePayer, () => { + zkapp.currentSlot.requireBetween( + UInt32.from(0), + UInt32.from(UInt32.MAXINT()) + ); + zkapp.requireSignature(); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await tx.sign([feePayerKey, zkappKey]).send(); + expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); + }); + it('get + assertNothing should not throw', async () => { let nonce = zkapp.account.nonce.get(); let tx = await Mina.transaction(feePayer, () => { @@ -132,6 +188,21 @@ describe('preconditions', () => { expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); }); + it('get + requireNothing should not throw', async () => { + let nonce = zkapp.account.nonce.get(); + let tx = await Mina.transaction(feePayer, () => { + for (let precondition of implemented) { + precondition().get(); + precondition().requireNothing(); + } + zkapp.requireSignature(); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await tx.sign([feePayerKey, zkappKey]).send(); + // check that tx was applied, by checking nonce was incremented + expect(zkapp.account.nonce.get()).toEqual(nonce.add(1)); + }); + it('get + manual precondition should not throw', async () => { // we only test this for a couple of preconditions let nonce = zkapp.account.nonce.get(); @@ -172,6 +243,19 @@ describe('preconditions', () => { } }); + it('unsatisfied requireEquals should be rejected (numbers)', async () => { + for (let precondition of implementedNumber) { + await expect(async () => { + let tx = await Mina.transaction(feePayer, () => { + let p = precondition().get(); + precondition().requireEquals(p.add(1) as any); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await tx.sign([feePayerKey]).send(); + }).rejects.toThrow(/unsatisfied/); + } + }); + it('unsatisfied assertEquals should be rejected (booleans)', async () => { for (let precondition of implementedBool) { let tx = await Mina.transaction(feePayer, () => { @@ -185,6 +269,19 @@ describe('preconditions', () => { } }); + it('unsatisfied requireEquals should be rejected (booleans)', async () => { + for (let precondition of implementedBool) { + let tx = await Mina.transaction(feePayer, () => { + let p = precondition().get(); + precondition().requireEquals(p.not()); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await expect(tx.sign([feePayerKey]).send()).rejects.toThrow( + /unsatisfied/ + ); + } + }); + it('unsatisfied assertEquals should be rejected (public key)', async () => { let publicKey = PublicKey.from({ x: Field(-1), isOdd: Bool(false) }); let tx = await Mina.transaction(feePayer, () => { @@ -194,6 +291,15 @@ describe('preconditions', () => { await expect(tx.sign([feePayerKey]).send()).rejects.toThrow(/unsatisfied/); }); + it('unsatisfied requireEquals should be rejected (public key)', async () => { + let publicKey = PublicKey.from({ x: Field(-1), isOdd: Bool(false) }); + let tx = await Mina.transaction(feePayer, () => { + zkapp.account.delegate.requireEquals(publicKey); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await expect(tx.sign([feePayerKey]).send()).rejects.toThrow(/unsatisfied/); + }); + it('unsatisfied assertBetween should be rejected', async () => { for (let precondition of implementedWithRange) { let tx = await Mina.transaction(feePayer, () => { @@ -207,6 +313,19 @@ describe('preconditions', () => { } }); + it('unsatisfied requireBetween should be rejected', async () => { + for (let precondition of implementedWithRange) { + let tx = await Mina.transaction(feePayer, () => { + let p: any = precondition().get(); + precondition().requireBetween(p.add(20), p.add(30)); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await expect(tx.sign([feePayerKey]).send()).rejects.toThrow( + /unsatisfied/ + ); + } + }); + it('unsatisfied currentSlot.assertBetween should be rejected', async () => { let tx = await Mina.transaction(feePayer, () => { zkapp.currentSlot.assertBetween(UInt32.from(20), UInt32.from(30)); @@ -215,6 +334,14 @@ describe('preconditions', () => { await expect(tx.sign([feePayerKey]).send()).rejects.toThrow(/unsatisfied/); }); + it('unsatisfied currentSlot.requireBetween should be rejected', async () => { + let tx = await Mina.transaction(feePayer, () => { + zkapp.currentSlot.requireBetween(UInt32.from(20), UInt32.from(30)); + AccountUpdate.attachToTransaction(zkapp.self); + }); + await expect(tx.sign([feePayerKey]).send()).rejects.toThrow(/unsatisfied/); + }); + // TODO: is this a gotcha that should be addressed? // the test below fails, so it seems that nonce is applied successfully with a WRONG precondition.. // however, this is just because `zkapp.sign()` overwrites the nonce precondition with one that is satisfied diff --git a/src/lib/precondition.ts b/src/lib/precondition.ts index 53e044f6ed..ac0bea3e79 100644 --- a/src/lib/precondition.ts +++ b/src/lib/precondition.ts @@ -49,11 +49,14 @@ function Network(accountUpdate: AccountUpdate): Network { let slot = network.globalSlotSinceGenesis.get(); return globalSlotToTimestamp(slot); }, - getAndAssertEquals() { - let slot = network.globalSlotSinceGenesis.getAndAssertEquals(); + getAndRequireEquals() { + let slot = network.globalSlotSinceGenesis.getAndRequireEquals(); return globalSlotToTimestamp(slot); }, - assertEquals(value: UInt64) { + getAndAssertEquals() { + return this.getAndRequireEquals(); + }, + requireEquals(value: UInt64) { let { genesisTimestamp, slotTime } = Mina.activeInstance.getNetworkConstants(); let slot = timestampToGlobalSlot( @@ -61,14 +64,26 @@ function Network(accountUpdate: AccountUpdate): Network { `Timestamp precondition unsatisfied: the timestamp can only equal numbers of the form ${genesisTimestamp} + k*${slotTime},\n` + `i.e., the genesis timestamp plus an integer number of slots.` ); - return network.globalSlotSinceGenesis.assertEquals(slot); + return network.globalSlotSinceGenesis.requireEquals(slot); }, - assertBetween(lower: UInt64, upper: UInt64) { + assertEquals(value: UInt64) { + return this.requireEquals(value); + }, + requireBetween(lower: UInt64, upper: UInt64) { let [slotLower, slotUpper] = timestampToGlobalSlotRange(lower, upper); - return network.globalSlotSinceGenesis.assertBetween(slotLower, slotUpper); + return network.globalSlotSinceGenesis.requireBetween( + slotLower, + slotUpper + ); + }, + assertBetween(lower: UInt64, upper: UInt64) { + return this.requireBetween(lower, upper); + }, + requireNothing() { + return network.globalSlotSinceGenesis.requireNothing(); }, assertNothing() { - return network.globalSlotSinceGenesis.assertNothing(); + return this.requireNothing(); }, }; return { ...network, timestamp }; @@ -118,7 +133,7 @@ function updateSubclass( function CurrentSlot(accountUpdate: AccountUpdate): CurrentSlot { let context = getPreconditionContextExn(accountUpdate); return { - assertBetween(lower: UInt32, upper: UInt32) { + requireBetween(lower: UInt32, upper: UInt32) { context.constrained.add('validWhile'); let property: RangeCondition = accountUpdate.body.preconditions.validWhile; @@ -126,6 +141,9 @@ function CurrentSlot(accountUpdate: AccountUpdate): CurrentSlot { property.value.lower = lower; property.value.upper = upper; }, + assertBetween(lower: UInt32, upper: UInt32) { + this.requireBetween(lower, upper); + }, }; } @@ -193,7 +211,7 @@ function preconditionSubClassWithRange< ) { return { ...preconditionSubclass(accountUpdate, longKey, fieldType as any, context), - assertBetween(lower: any, upper: any) { + requireBetween(lower: any, upper: any) { context.constrained.add(longKey); let property: RangeCondition = getPath( accountUpdate.body.preconditions, @@ -203,6 +221,9 @@ function preconditionSubClassWithRange< property.value.lower = lower; property.value.upper = upper; }, + assertBetween(lower: any, upper: any) { + this.requireBetween(lower, upper); + }, }; } @@ -232,12 +253,15 @@ function preconditionSubclass< fieldType )) as U; }, - getAndAssertEquals() { + getAndRequireEquals() { let value = obj.get(); - obj.assertEquals(value); + obj.requireEquals(value); return value; }, - assertEquals(value: U) { + getAndAssertEquals() { + return this.getAndRequireEquals(); + }, + requireEquals(value: U) { context.constrained.add(longKey); let property = getPath( accountUpdate.body.preconditions, @@ -255,9 +279,15 @@ function preconditionSubclass< setPath(accountUpdate.body.preconditions, longKey, value); } }, - assertNothing() { + assertEquals(value: U) { + this.requireEquals(value); + }, + requireNothing() { context.constrained.add(longKey); }, + assertNothing() { + this.requireNothing(); + }, }; return obj; } @@ -437,6 +467,10 @@ type Account = PreconditionClassType & Update; type CurrentSlotPrecondition = Preconditions['validWhile']; type CurrentSlot = { + requireBetween(lower: UInt32, upper: UInt32): void; + /** + * @deprecated use `requireBetween(lower: U, upper: U)` which is equivalent + */ assertBetween(lower: UInt32, upper: UInt32): void; }; @@ -452,11 +486,27 @@ type PreconditionBaseTypes = { type PreconditionSubclassType = { get(): U; + getAndRequireEquals(): U; + /** + * @deprecated use `getAndRequireEquals()` which is equivalent + */ getAndAssertEquals(): U; + requireEquals(value: U): void; + /** + * @deprecated use `requireEquals(value: U)` which is equivalent + */ assertEquals(value: U): void; + requireNothing(): void; + /** + * @deprecated use `requireNothing()` which is equivalent + */ assertNothing(): void; }; type PreconditionSubclassRangeType = PreconditionSubclassType & { + requireBetween(lower: U, upper: U): void; + /** + * @deprecated use `requireBetween(lower: U, upper: U)` which is equivalent + */ assertBetween(lower: U, upper: U): void; }; From bb9b6df175e9539b71b9cf627e5d94ece116111c Mon Sep 17 00:00:00 2001 From: Saurabh Patil <55076843+Saurabhpatil-dev@users.noreply.github.com> Date: Thu, 23 Nov 2023 00:48:19 +0530 Subject: [PATCH 2/3] :hammer: updated the deprecated message --- src/lib/precondition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/precondition.ts b/src/lib/precondition.ts index ac0bea3e79..25b10b9031 100644 --- a/src/lib/precondition.ts +++ b/src/lib/precondition.ts @@ -469,7 +469,7 @@ type CurrentSlotPrecondition = Preconditions['validWhile']; type CurrentSlot = { requireBetween(lower: UInt32, upper: UInt32): void; /** - * @deprecated use `requireBetween(lower: U, upper: U)` which is equivalent + * @deprecated use `requireBetween(lower: UInt32, upper: UInt32)` which is equivalent */ assertBetween(lower: UInt32, upper: UInt32): void; }; From 43f1efdfc797910b144d982fd5d997a8a0dbd706 Mon Sep 17 00:00:00 2001 From: Saurabh Patil <55076843+Saurabhpatil-dev@users.noreply.github.com> Date: Fri, 24 Nov 2023 17:53:05 +0530 Subject: [PATCH 3/3] :memo: Adding logs into ChangeLog file --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6201c24a15..dfd3e03aac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - `this.x.getAndAssertEquals()` is now `this.x.getAndRequireEquals()` https://github.com/o1-labs/o1js/pull/1263 - `this.x.assertEquals(x)` is now `this.x.requireEquals(x)` https://github.com/o1-labs/o1js/pull/1263 - `this.x.assertNothing()` is now `this.x.requireNothing()` https://github.com/o1-labs/o1js/pull/1263 + - `this.account.x.assertBetween()` is now `this.account.x.requireBetween()` https://github.com/o1-labs/o1js/pull/1265 + - `this.account.x.assertEquals(x)` is now `this.account.x.requireEquals(x)` https://github.com/o1-labs/o1js/pull/1265 + - `this.account.x.assertNothing()` is now `this.account.x.requireNothing()` https://github.com/o1-labs/o1js/pull/1265 + - `this.currentSlot.assertBetween(x,y)` is now `this.currentSlot.requireBetween(x,y)` https://github.com/o1-labs/o1js/pull/1265 + - `this.network.x.getAndAssertEquals()` is now `this.network.x.getAndRequireEquals()` https://github.com/o1-labs/o1js/pull/1265 ## [0.14.2](https://github.com/o1-labs/o1js/compare/26363465d...1ad7333e9e)