Skip to content

Commit 123a370

Browse files
committed
improve error messages and pass optional failure strings
1 parent b38a3ad commit 123a370

File tree

1 file changed

+100
-57
lines changed

1 file changed

+100
-57
lines changed

src/lib/provable/dynamic-array.ts

Lines changed: 100 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ function DynamicArray<
6464
let innerType: Provable<ProvableValue, Value> = ProvableType.get(type);
6565

6666
// assert capacity bounds
67-
assert(capacity >= 0, 'capacity must be >= 0');
68-
assert(capacity < 2 ** 16, 'capacity must be < 2^16');
67+
assert(capacity >= 0, 'DynamicArray(): capacity must be >= 0');
68+
assert(capacity < 2 ** 16, 'DynamicArray(): capacity must be < 2^16');
6969

7070
class DynamicArray_ extends DynamicArrayBase<ProvableValue, Value> {
7171
get innerType() {
@@ -101,10 +101,10 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
101101

102102
// properties to override in subclass
103103
get innerType(): Provable<ProvableValue, Value> {
104-
throw Error('Inner type must be defined in a subclass.');
104+
throw Error('Inner type must be defined in a subclass');
105105
}
106106
static get capacity(): number {
107-
throw Error('Capacity must be defined in a subclass.');
107+
throw Error('Capacity must be defined in a subclass');
108108
}
109109

110110
// derived property
@@ -147,12 +147,12 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
147147

148148
assert(
149149
a.length <= this.capacity,
150-
'Array must not exceed capacity'
150+
'DynamicArray(): array must not exceed capacity'
151151
);
152152
if (length?.isConstant()) {
153153
assert(
154154
l.toBigInt() <= BigInt(a.length),
155-
'length must be at most as long as the array'
155+
'DynamicArray(): length must be at most as long as the array'
156156
);
157157
}
158158

@@ -164,14 +164,19 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
164164
* In-circuit assertion that the given index is within the bounds of the
165165
* dynamic array.
166166
* Asserts 0 <= i < this.length, using a cached check that's not
167-
* duplicated when doing it on the same variable multiple times.
167+
* duplicated when doing it on the same variable multiple times, failing
168+
* with an error message otherwise.
169+
*
170+
* @param i - the index to check
171+
* @param message - optional error message to use in case the assertion fails
168172
*/
169-
assertIndexInRange(i: Field): void {
173+
assertIndexInRange(i: Field, message?: string): void {
174+
let errorMessage = message ?? `assertIndexInRange(): index ${i} must be in range [0, ${this.length}]`;
170175
if (!this.#indicesInRange.has(i)) {
171176
if (i.isConstant() && this.length.isConstant()) {
172-
assert(i.toBigInt() < this.length.toBigInt(), 'assertIndexInRange');
177+
assert(i.toBigInt() < this.length.toBigInt(), errorMessage);
173178
}
174-
i.lessThan(this.length).assertTrue();
179+
i.assertLessThan(this.length, errorMessage);
175180
this.#indicesInRange.add(i);
176181
}
177182
}
@@ -232,8 +237,9 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
232237
/**
233238
* Sets a value at index i and proves that the index is in the array.
234239
*/
235-
set(i: Field, value: ProvableValue): void {
236-
this.assertIndexInRange(i);
240+
set(i: Field, value: ProvableValue, message?: string): void {
241+
let errorMessage = message ?? `set(): index ${i} must be in range [0, ${this.length}]`;
242+
this.assertIndexInRange(i, errorMessage);
237243
this.setOrDoNothing(i, value);
238244
}
239245

@@ -286,9 +292,14 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
286292
*
287293
* **Note**: this doesn't cost constraints, but currently doesn't preserve any
288294
* cached constraints.
295+
*
296+
* @param capacity - the new capacity of the array
289297
*/
290-
growCapacityTo(capacity: number): DynamicArray<ProvableValue, Value> {
291-
assert(capacity >= this.capacity, 'new capacity must be greater or equal');
298+
growCapacityTo(capacity: number, message?: string): DynamicArray<ProvableValue, Value> {
299+
let errorMessage =
300+
message ??
301+
`growCapacityTo: new capacity ${capacity} must be greater than current capacity ${this.capacity}`;
302+
assert(capacity >= this.capacity,errorMessage);
292303
let NewArray = DynamicArray(this.innerType, { capacity });
293304
let NULL = ProvableType.synthesize(this.innerType);
294305
let array = pad(this.array, capacity, NULL);
@@ -302,20 +313,19 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
302313
*
303314
* **Note**: this doesn't cost constraints, but currently doesn't preserve any
304315
* cached constraints.
316+
*
317+
* @param increment - the amount to increase the capacity by
305318
*/
306319
growCapacityBy(increment: number): DynamicArray<ProvableValue> {
307320
return this.growCapacityTo(this.capacity + increment);
308321
}
309322

310323
/**
311324
* Increments the length of the current array by n elements, checking that the
312-
* new length is within the capacity.
313-
*
314-
* An optional error message can be provided to be used in case the inner
315-
* assertion fails.
325+
* new length is within the capacity, failing with the error message otherwise.
316326
*
317-
* @param n
318-
* @param message
327+
* @param n - the number of elements to increase the length by
328+
* @param message - optional error message to use in case the assertion fails
319329
*/
320330
increaseLengthBy(n: Field, message?: string): void {
321331
let errorMessage =
@@ -329,13 +339,11 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
329339

330340
/**
331341
* Decrements the length of the current array by `n` elements, checking that
332-
* the `n` is less or equal than the current length.
342+
* the `n` is less or equal than the current length, failing with the error
343+
* message otherwise.
333344
*
334-
* An optional error message can be provided to be used in case the inner
335-
* assertion fails.
336-
*
337-
* @param n
338-
* @param message
345+
* @param n - the number of elements to decrease the length by
346+
* @param message - optional error message to use in case the assertion fails
339347
*/
340348
decreaseLengthBy(n: Field, message?: string): void {
341349
let errorMessage =
@@ -354,13 +362,16 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
354362
* An optional error message can be provided to be used in case the inner
355363
* assertion fails.
356364
*
357-
* @param newLength
358-
* @param message
365+
* @param newLength - the new length to set the array to
366+
* @param message - optional error message
359367
*
360368
* **Warning**: This does not change (add nor remove) the values of the array.
361369
*/
362370
setLengthTo(n: Field, message?: string): void {
363-
n.assertLessThanOrEqual(new Field(this.capacity));
371+
let errorMessage =
372+
message ??
373+
`setLengthTo: cannot set length to ${n} because it exceeds capacity ${this.capacity}`;
374+
n.assertLessThanOrEqual(new Field(this.capacity), errorMessage);
364375
this.length = n;
365376
}
366377

@@ -375,10 +386,16 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
375386
* array = array.growCapacityhBy(1);
376387
* array.push(value);
377388
* ```
389+
*
390+
* @param value - the value to push into the array
391+
* @param message - optional error message to use in case the assertion fails
378392
*/
379-
push(value: ProvableValue): void {
393+
push(value: ProvableValue, message?: string): void {
394+
let errorMessage =
395+
message ??
396+
`push(): cannot push value because it would exceed capacity ${this.capacity}.`;
380397
let oldLength = this.length;
381-
this.increaseLengthBy(new Field(1));
398+
this.increaseLengthBy(new Field(1), errorMessage);
382399
this.setOrDoNothing(oldLength, value);
383400
}
384401

@@ -387,11 +404,16 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
387404
* by n. If no amount is provided, only one element is popped. The popped
388405
* positions are set to NULL values.
389406
*
390-
* @param n
407+
* @param n - the number of elements to pop (one if not provided)
408+
* @param message - optional error message to use in case the assertion fails
391409
*/
392-
pop(n?: Field): void {
410+
pop(n?: Field, message?: string): void {
411+
let errorMessage =
412+
message ??
413+
`pop(): cannot pop ${n} elements because the length is smaller`;
414+
393415
let dec = n !== undefined ? n : new Field(1);
394-
this.decreaseLengthBy(dec);
416+
this.decreaseLengthBy(dec, errorMessage);
395417

396418
let NULL: ProvableValue = ProvableType.synthesize(this.innerType);
397419
if (n !== undefined) {
@@ -420,12 +442,17 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
420442
}
421443

422444
/**
423-
* Shifts all elements of the array to the left by n positions, reducing the
424-
* length by n, which must be less than or equal to the current length.
445+
* Shifts all elements of the array to the left by `n` positions, reducing
446+
* the length by `n`, which must be less than or equal to the current length
447+
* (failing with an error message otherwise).
425448
*
426-
* @param n
449+
* @param n - the number of positions to shift left
450+
* @param message - optional error message to use in case the assertion fails
427451
*/
428-
shiftLeft(n: Field): void {
452+
shiftLeft(n: Field, message?: string): void {
453+
let errorMessage =
454+
message ??
455+
`shiftLeft(): cannot shift left because provided n would exceed current length.`;
429456
let NULL = ProvableType.synthesize(this.innerType);
430457
for (let i = 0; i < this.capacity; i++) {
431458
let offset = new Field(i).add(n);
@@ -436,18 +463,23 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
436463
NULL
437464
);
438465
}
439-
this.decreaseLengthBy(n);
466+
this.decreaseLengthBy(n, errorMessage);
440467
}
441468

442469
/**
443-
* Shifts all elements of the array to the right by n positions, increasing
444-
* the length by n, which must result in less than or equal to the capacity.
445-
* The new elements on the left are set to NULL values.
470+
* Shifts all elements of the array to the right by `n` positions, increasing
471+
* the length by `n`, which must result in less than or equal to the capacity
472+
* (failing with an error message otherwise). The new elements on the left are
473+
* set to NULL values.
446474
*
447-
* @param n
475+
* @param n - the number of positions to shift right
476+
* @param message - optional error message to use in case the assertion fails
448477
*/
449-
shiftRight(n: Field): void {
450-
this.increaseLengthBy(n);
478+
shiftRight(n: Field, message?: string): void {
479+
let errorMessage =
480+
message ??
481+
`shiftRight(): cannot shift right because provided n would exceed capacity ${this.capacity}`;
482+
this.increaseLengthBy(n, errorMessage);
451483
let NULL = ProvableType.synthesize(this.innerType);
452484

453485
for (let i = this.capacity - 1; i >= 0; i--) {
@@ -481,16 +513,17 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
481513
* provided, it defaults to 0. If `end` is not provided, it defaults to the
482514
* length of the array.
483515
*
484-
* @param start
485-
* @param end
486-
* @returns
516+
* @param start - the starting index of the slice (inclusive)
517+
* @param end - the ending index of the slice (exclusive)
518+
*
519+
* @returns a new DynamicArray instance with the sliced values
487520
*/
488521
slice(start?: Field, end?: Field): DynamicArray<ProvableValue, Value> {
489522
start ??= new Field(0);
490523
end ??= this.length;
491524
let sliced = this.copy();
492-
sliced.shiftLeft(start);
493-
sliced.pop(this.length.sub(end));
525+
sliced.shiftLeft(start, `slice(): provided start is greater than current length`);
526+
sliced.pop(this.length.sub(end), `slice(): provided end is greater than current length`);
494527
return sliced;
495528
}
496529

@@ -499,8 +532,9 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
499532
* dynamic array with the values of both arrays. The capacity of the new array
500533
* is the sum of the capacities of the two arrays.
501534
*
502-
* @param other
503-
* @returns
535+
* @param other - the dynamic array to concatenate
536+
*
537+
* @returns a new DynamicArray instance with the concatenated values
504538
*/
505539
concat(other: DynamicArray<ProvableValue, Value>): DynamicArray<ProvableValue, Value> {
506540
let res = this.growCapacityTo(this.capacity + other.capacity);
@@ -523,12 +557,16 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
523557
* the right by one. The length of the array is increased by one, which must
524558
* result in less than or equal to the capacity.
525559
*
526-
* @param i
527-
* @param value
560+
* @param i - the index at which to insert the value
561+
* @param value - the value to insert
562+
* @param message - optional error message to use in case the assertion fails
528563
*/
529-
insert(index: Field, value: ProvableValue): void {
564+
insert(index: Field, value: ProvableValue, message?: string): void {
565+
let errorMessage =
566+
message ??
567+
`insert(): cannot insert value at index ${index} because it would exceed capacity ${this.capacity}.`;
530568
const right = this.slice(index, this.length);
531-
this.increaseLengthBy(new Field(1));
569+
this.increaseLengthBy(new Field(1), errorMessage);
532570
this.set(index, value);
533571
for (let i = 0; i < this.capacity; i++) {
534572
let offset = new Field(i).sub(index).sub(new Field(1));
@@ -544,7 +582,7 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
544582
/**
545583
* Checks whether the dynamic array includes a value.
546584
*
547-
* @param value
585+
* @param value - the value to check for inclusion in the array
548586
* @returns
549587
*/
550588
includes(value: ProvableValue): Bool {
@@ -596,6 +634,11 @@ class DynamicArrayBase<ProvableValue = any, Value = any> {
596634
return mask;
597635
}
598636

637+
/**
638+
* Converts the current instance of the dynamic array to a plain array of values.
639+
*
640+
* @returns An array of values representing the elements in the dynamic array.
641+
*/
599642
toValue(): Value[] {
600643
return (this.constructor as any as { provable: Provable<any, Value[]> }).provable.toValue(this);
601644
}

0 commit comments

Comments
 (0)