Skip to content

Commit d1e7788

Browse files
committed
Added Safe.fromField and .max
1 parent 9cc3fb6 commit d1e7788

File tree

6 files changed

+116
-63
lines changed

6 files changed

+116
-63
lines changed

packages/library/src/math/PrecisionHelper.ts

Lines changed: 0 additions & 34 deletions
This file was deleted.

packages/library/src/math/UInt.ts

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// eslint-disable-next-line max-len
22
/* eslint-disable @typescript-eslint/no-magic-numbers,prefer-const,id-length,no-underscore-dangle */
3-
import { Bool, Field, Provable, Struct } from "o1js";
3+
import { Bool, Field, Provable, Struct, UInt64 as O1UInt64 } from "o1js";
44
import { assert } from "@proto-kit/protocol";
55
// @ts-ignore
66
import bigintSqrt from "bigint-isqrt";
@@ -15,16 +15,26 @@ const errors = {
1515
};
1616

1717
export type UIntConstructor<BITS extends number> = {
18-
// new(value: Field | { value: Field }): UIntX<BITS>;
1918
from(x: UInt<BITS> | bigint | number | string): UInt<BITS>;
2019
check(x: { value: Field }): void;
2120
get zero(): UInt<BITS>;
21+
get max(): UInt<BITS>;
2222

2323
Unsafe: {
2424
fromField(x: Field): UInt<BITS>;
2525
};
26+
Safe: {
27+
fromField(x: Field): UInt<BITS>;
28+
};
2629
};
2730

31+
/**
32+
* UInt is a base class for all soft-failing UInt* implementations.
33+
* It has to be overridden for every bitlength that should be available.
34+
*
35+
* For this, the developer has to create a subclass of UInt implementing the
36+
* static methods from interface UIntConstructor
37+
*/
2838
export abstract class UInt<BITS extends number> extends Struct({
2939
value: Field,
3040
}) {
@@ -51,6 +61,18 @@ export abstract class UInt<BITS extends number> extends Struct({
5161
return Field((1n << BigInt(numBits)) - 1n);
5262
}
5363

64+
private checkConstant(x: Field) {
65+
if (!x.isConstant()) return x;
66+
let xBig = x.toBigInt();
67+
const bits = this.numBits();
68+
if (xBig < 0n || xBig >= 1n << BigInt(this.numBits())) {
69+
throw Error(
70+
`UInt${bits}: Expected number between 0 and 2^${bits} - 1, got ${xBig}`
71+
);
72+
}
73+
return x;
74+
}
75+
5476
public constructor(value: { value: Field }) {
5577
super(value);
5678

@@ -62,6 +84,8 @@ export abstract class UInt<BITS extends number> extends Struct({
6284
if (bits === 256) {
6385
throw errors.usageWith256BitsForbidden();
6486
}
87+
88+
this.checkConstant(value.value);
6589
}
6690

6791
public abstract numBits(): BITS;
@@ -393,24 +417,29 @@ export abstract class UInt<BITS extends number> extends Struct({
393417
UInt.assertionFunction(this.equals(y), message);
394418
}
395419

396-
// /**
397-
// * Turns the {@link UIntX} into a {@link UInt64}, asserting that it fits in 32 bits.
398-
// */
399-
// public toUInt64() {
400-
// let uint64 = new UInt64(this.value);
401-
// UInt64.check(uint64);
402-
// return uint64;
403-
// }
404-
//
405-
// /**
406-
// * Turns the {@link UIntX} into a {@link UInt64}, clamping to the 64 bits range if it's too large.
407-
// */
408-
// public toUInt64Clamped() {
409-
// let max = (1n << 64n) - 1n;
410-
// return Provable.if(
411-
// this.greaterThan(UIntX.from(max)),
412-
// UInt64.from(max),
413-
// new UInt64(this.value)
414-
// );
415-
// }
420+
/**
421+
* Turns the {@link UInt} into a o1js {@link UInt64}, asserting that it fits in 32 bits.
422+
*/
423+
public toO1UInt64() {
424+
let uint64 = new O1UInt64(this.value);
425+
O1UInt64.check(uint64);
426+
return uint64;
427+
}
428+
429+
/**
430+
* Turns the {@link UInt} into a o1js {@link UInt64}, clamping to the 64 bits range if it's too large.
431+
*/
432+
public toO1UInt64Clamped() {
433+
if (this.numBits() <= 64) {
434+
return new O1UInt64(this.value);
435+
} else {
436+
let max = (1n << 64n) - 1n;
437+
return Provable.if(
438+
// We know that BITS is >64 bits, so we can skip range checks for max
439+
this.greaterThan(this.fromField(Field(max))),
440+
O1UInt64.from(max),
441+
new O1UInt64(this.value)
442+
);
443+
}
444+
}
416445
}
Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Field } from "o1js";
22
import { UIntConstructor, UInt } from "./UInt";
3+
import { UInt224 } from "./UInt224";
34

45
export class UInt112 extends UInt<112> {
56
public static Unsafe = {
@@ -8,20 +9,32 @@ export class UInt112 extends UInt<112> {
89
},
910
};
1011

12+
public static Safe = {
13+
fromField(value: Field) {
14+
const uint = new UInt112({ value });
15+
UInt112.check(uint);
16+
return uint;
17+
},
18+
};
19+
1120
public static check(x: { value: Field }) {
1221
const actual = x.value.rangeCheckHelper(112);
1322
UInt.assertionFunction(actual.equals(x.value));
1423
}
1524

16-
public static from(x: UInt<112> | bigint | number | string): UInt112 {
17-
if (x instanceof UInt) {
25+
public static from(x: UInt112 | bigint | number | string): UInt112 {
26+
if (x instanceof UInt112) {
1827
return x;
1928
}
2029
return new UInt112({ value: UInt.checkConstant(Field(x), 112) });
2130
}
2231

2332
public static get zero() {
24-
return UInt112.Unsafe.fromField(Field(0))
33+
return UInt112.Unsafe.fromField(Field(0));
34+
}
35+
36+
public static get max() {
37+
return UInt112.Unsafe.fromField(UInt.maxIntField(112));
2538
}
2639

2740
public constructorReference(): UIntConstructor<112> {
@@ -31,4 +44,8 @@ export class UInt112 extends UInt<112> {
3144
public numBits() {
3245
return 112 as const;
3346
}
47+
48+
public toUInt224(): UInt224 {
49+
return UInt224.Unsafe.fromField(this.value);
50+
}
3451
}

packages/library/src/math/UInt224.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Field, UInt64 } from "o1js";
1+
import { Field } from "o1js";
22
import { UIntConstructor, UInt } from "./UInt";
33

44
export class UInt224 extends UInt<224> {
@@ -8,6 +8,14 @@ export class UInt224 extends UInt<224> {
88
},
99
};
1010

11+
public static Safe = {
12+
fromField(value: Field) {
13+
const uint = new UInt224({ value });
14+
UInt224.check(uint);
15+
return uint;
16+
},
17+
};
18+
1119
public static check(x: { value: Field }) {
1220
const actual = x.value.rangeCheckHelper(224);
1321
UInt.assertionFunction(actual.equals(x.value));
@@ -21,7 +29,11 @@ export class UInt224 extends UInt<224> {
2129
}
2230

2331
public static get zero() {
24-
return UInt224.Unsafe.fromField(Field(0))
32+
return UInt224.Unsafe.fromField(Field(0));
33+
}
34+
35+
public static get max() {
36+
return UInt224.Unsafe.fromField(UInt.maxIntField(224));
2537
}
2638

2739
public constructorReference(): UIntConstructor<224> {

packages/library/src/math/UInt32.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Field } from "o1js";
22
import { UIntConstructor, UInt } from "./UInt";
3+
import { UInt64 } from "./UInt64";
34

45
export class UInt32 extends UInt<32> {
56
public static Unsafe = {
@@ -8,6 +9,14 @@ export class UInt32 extends UInt<32> {
89
},
910
};
1011

12+
public static Safe = {
13+
fromField(value: Field) {
14+
const uint = new UInt32({ value });
15+
UInt32.check(uint);
16+
return uint;
17+
},
18+
};
19+
1120
public static check(x: { value: Field }) {
1221
const actual = x.value.rangeCheckHelper(32);
1322
UInt.assertionFunction(actual.equals(x.value));
@@ -21,7 +30,11 @@ export class UInt32 extends UInt<32> {
2130
}
2231

2332
public static get zero() {
24-
return UInt32.Unsafe.fromField(Field(0))
33+
return UInt32.Unsafe.fromField(Field(0));
34+
}
35+
36+
public static get max() {
37+
return UInt32.Unsafe.fromField(UInt.maxIntField(32));
2538
}
2639

2740
public constructorReference(): UIntConstructor<32> {
@@ -31,4 +44,8 @@ export class UInt32 extends UInt<32> {
3144
public numBits(): 32 {
3245
return 32;
3346
}
47+
48+
public toUInt64(): UInt64 {
49+
return UInt64.Unsafe.fromField(this.value);
50+
}
3451
}

packages/library/src/math/UInt64.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ export class UInt64 extends UInt<64> {
88
},
99
};
1010

11+
public static Safe = {
12+
fromField(value: Field) {
13+
const uint = new UInt64({ value });
14+
UInt64.check(uint);
15+
return uint;
16+
},
17+
};
18+
1119
public static check(x: { value: Field }) {
1220
const actual = x.value.rangeCheckHelper(64);
1321
UInt.assertionFunction(actual.equals(x.value));
@@ -21,7 +29,11 @@ export class UInt64 extends UInt<64> {
2129
}
2230

2331
public static get zero() {
24-
return UInt64.Unsafe.fromField(Field(0))
32+
return UInt64.Unsafe.fromField(Field(0));
33+
}
34+
35+
public static get max() {
36+
return UInt64.Unsafe.fromField(UInt.maxIntField(64));
2537
}
2638

2739
public constructorReference(): UIntConstructor<64> {

0 commit comments

Comments
 (0)