Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated assert* to require* for rest pending precondition apis. #1247 #1265

Merged
merged 4 commits into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
127 changes: 127 additions & 0 deletions src/lib/precondition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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, () => {
Expand All @@ -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, () => {
Expand All @@ -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, () => {
Expand All @@ -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();
Expand Down Expand Up @@ -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, () => {
Expand All @@ -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, () => {
Expand All @@ -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, () => {
Expand All @@ -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));
Expand All @@ -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
Expand Down
76 changes: 63 additions & 13 deletions src/lib/precondition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,26 +49,41 @@ 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(
value,
`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 };
Expand Down Expand Up @@ -118,14 +133,17 @@ function updateSubclass<K extends keyof Update>(
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<UInt32> =
accountUpdate.body.preconditions.validWhile;
property.isSome = Bool(true);
property.value.lower = lower;
property.value.upper = upper;
},
assertBetween(lower: UInt32, upper: UInt32) {
this.requireBetween(lower, upper);
},
};
}

Expand Down Expand Up @@ -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<any> = getPath(
accountUpdate.body.preconditions,
Expand All @@ -203,6 +221,9 @@ function preconditionSubClassWithRange<
property.value.lower = lower;
property.value.upper = upper;
},
assertBetween(lower: any, upper: any) {
this.requireBetween(lower, upper);
},
};
}

Expand Down Expand Up @@ -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,
Expand All @@ -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;
}
Expand Down Expand Up @@ -437,6 +467,10 @@ type Account = PreconditionClassType<AccountPrecondition> & Update;

type CurrentSlotPrecondition = Preconditions['validWhile'];
type CurrentSlot = {
requireBetween(lower: UInt32, upper: UInt32): void;
/**
* @deprecated use `requireBetween(lower: UInt32, upper: UInt32)` which is equivalent
*/
assertBetween(lower: UInt32, upper: UInt32): void;
};

Expand All @@ -452,11 +486,27 @@ type PreconditionBaseTypes<T> = {

type PreconditionSubclassType<U> = {
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<U> = PreconditionSubclassType<U> & {
requireBetween(lower: U, upper: U): void;
/**
* @deprecated use `requireBetween(lower: U, upper: U)` which is equivalent
*/
assertBetween(lower: U, upper: U): void;
};

Expand Down
Loading