Skip to content

Commit

Permalink
Merge pull request #1461 from o1-labs/fix/field-to-from-254bits
Browse files Browse the repository at this point in the history
Implement to/fromBits in TS, and set max length to 254 bits
  • Loading branch information
jackryanservia authored Mar 12, 2024
2 parents 7f750b6 + e5cd926 commit 72b4d12
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 38 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
As a replacement, `UInt64.Unsafe.fromField()` was introduced
- This prevents you from accidentally creating a `UInt64` without proving that it fits in 64 bits
- Equivalent changes were made to `UInt32`
- Fixed vulnerability in `Field.to/fromBits()` outlined in [#1023](https://github.com/o1-labs/o1js/issues/1023) by imposing a limit of 254 bits https://github.com/o1-labs/o1js/pull/1461

### Added

Expand Down
52 changes: 30 additions & 22 deletions src/lib/field.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Snarky, Provable } from '../snarky.js';
import { Snarky } from '../snarky.js';
import { Field as Fp } from '../provable/field-bigint.js';
import { defineBinable } from '../bindings/lib/binable.js';
import type { NonNegativeInteger } from '../bindings/crypto/non-negative.js';
import { asProver, inCheckedComputation } from './provable-context.js';
import { Bool } from './bool.js';
import { assert } from './errors.js';
import { Provable } from './provable.js';

// external API
export { Field };
Expand Down Expand Up @@ -908,34 +909,41 @@ class Field {
* Returns an array of {@link Bool} elements representing [little endian binary representation](https://en.wikipedia.org/wiki/Endianness) of this {@link Field} element.
*
* If you use the optional `length` argument, proves that the field element fits in `length` bits.
* The `length` has to be between 0 and 255 and the method throws if it isn't.
* The `length` has to be between 0 and 254 and the method throws if it isn't.
*
* **Warning**: The cost of this operation in a zk proof depends on the `length` you specify,
* which by default is 255 bits. Prefer to pass a smaller `length` if possible.
* which by default is 254 bits. Prefer to pass a smaller `length` if possible.
*
* @param length - the number of bits to fit the element. If the element does not fit in `length` bits, the functions throws an error.
*
* @return An array of {@link Bool} element representing little endian binary representation of this {@link Field}.
*/
toBits(length?: number) {
if (length !== undefined) checkBitLength('Field.toBits()', length);
toBits(length: number = 254) {
checkBitLength('Field.toBits()', length, 254);
if (this.isConstant()) {
let bits = Fp.toBits(this.toBigInt());
if (length !== undefined) {
if (bits.slice(length).some((bit) => bit))
throw Error(`Field.toBits(): ${this} does not fit in ${length} bits`);
return bits.slice(0, length).map((b) => new Bool(b));
}
return bits.map((b) => new Bool(b));
if (bits.slice(length).some((bit) => bit))
throw Error(`Field.toBits(): ${this} does not fit in ${length} bits`);
return bits.slice(0, length).map((b) => new Bool(b));
}
let [, ...bits] = Snarky.field.toBits(length ?? Fp.sizeInBits, this.value);
return bits.map((b) => new Bool(b));
let bits = Provable.witness(Provable.Array(Bool, length), () => {
let f = this.toBigInt();
return Array.from(
{ length },
(_, k) => new Bool(!!((f >> BigInt(k)) & 0x1n))
);
});
Field.fromBits(bits).assertEquals(
this,
`Field.toBits(): Input does not fit in ${length} bits`
);
return bits;
}

/**
* Convert a bit array into a {@link Field} element using [little endian binary representation](https://en.wikipedia.org/wiki/Endianness)
*
* The method throws if the given bits do not fit in a single Field element. A Field element can be at most 255 bits.
* The method throws if the given bits do not fit in a single Field element. In this case, no more than 254 bits are allowed because some 255 bit integers do not fit into a single Field element.
*
* **Important**: If the given `bytes` array is an array of `booleans` or {@link Bool} elements that all are `constant`, the resulting {@link Field} element will be a constant as well. Or else, if the given array is a mixture of constants and variables of {@link Bool} type, the resulting {@link Field} will be a variable as well.
*
Expand All @@ -944,20 +952,20 @@ class Field {
* @return A {@link Field} element matching the [little endian binary representation](https://en.wikipedia.org/wiki/Endianness) of the given `bytes` array.
*/
static fromBits(bits: (Bool | boolean)[]) {
let length = bits.length;
checkBitLength('Field.fromBits()', length);
const length = bits.length;
checkBitLength('Field.fromBits()', length, 254);
if (bits.every((b) => typeof b === 'boolean' || b.toField().isConstant())) {
let bits_ = bits
.map((b) => (typeof b === 'boolean' ? b : b.toBoolean()))
.concat(Array(Fp.sizeInBits - length).fill(false));
return new Field(Fp.fromBits(bits_));
}
let bitsVars = bits.map((b): FieldVar => {
if (typeof b === 'boolean') return b ? FieldVar[1] : FieldVar[0];
return b.toField().value;
});
let x = Snarky.field.fromBits([0, ...bitsVars]);
return new Field(x);
return bits
.map((b) => new Bool(b))
.reduce((acc, bit, idx) => {
const shift = 1n << BigInt(idx);
return acc.add(bit.toField().mul(shift));
}, Field.from(0)).seal();
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/lib/field.unit-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,9 @@ test(Random.field, Random.field, (x0, y0, assert) => {
Provable.asProver(() => assert(z.toBigInt() === Fp.mul(x0, y0)));

// toBits / fromBits
let bits = Fp.toBits(x0);
// Fp.toBits() returns 255 bits, but our new to/from impl only accepts <=254
// https://github.com/o1-labs/o1js/pull/1461
let bits = Fp.toBits(x0).slice(0, -1);
let x1 = Provable.witness(Field, () => Field.fromBits(bits));
let bitsVars = x1.toBits();
Provable.asProver(() =>
Expand Down
9 changes: 5 additions & 4 deletions src/lib/provable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
* - a namespace with tools for writing provable code
* - the main interface for types that can be used in provable code
*/
import { Field, Bool } from './core.js';
import { Bool } from './bool.js';
import { Field } from './field.js';
import { Provable as Provable_, Snarky } from '../snarky.js';
import type { FlexibleProvable, ProvableExtended } from './circuit-value.js';
import { Context } from './global-context.js';
Expand Down Expand Up @@ -254,7 +255,7 @@ function witness<T, S extends FlexibleProvable<T> = FlexibleProvable<T>>(
// }
return [0, ...fieldConstants];
});
fields = fieldVars.map(Field);
fields = fieldVars.map((x) => new Field(x));
} finally {
snarkContext.leave(id);
}
Expand Down Expand Up @@ -291,7 +292,7 @@ async function witnessAsync<
let fields = type.toFields(proverValue);
return fields.map((x) => x.toBigInt());
});
fields = fieldVars.map(Field);
fields = fieldVars.map((x) => new Field(x));
} finally {
snarkContext.leave(id);
}
Expand Down Expand Up @@ -440,7 +441,7 @@ function switch_<T, A extends FlexibleProvable<T>>(
if (mask.every((b) => b.toField().isConstant())) checkMask();
else Provable.asProver(checkMask);
let size = type.sizeInFields();
let fields = Array(size).fill(Field(0));
let fields = Array(size).fill(new Field(0));
for (let i = 0; i < nValues; i++) {
let valueFields = type.toFields(values[i]);
let maskField = mask[i].toField();
Expand Down
5 changes: 4 additions & 1 deletion src/lib/scalar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ class Scalar {
* **Warning**: The bits are interpreted as the bits of 2s + 1 + 2^255, where s is the Scalar.
*/
static fromBits(bits: Bool[]) {
return Scalar.fromFields(bits.map((b) => b.toField()));
return Scalar.fromFields([
...bits.map((b) => b.toField()),
...Array(Fq.sizeInBits - bits.length).fill(new Bool(false)),
]);
}

/**
Expand Down
20 changes: 10 additions & 10 deletions tests/vk-regression/vk-regression.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,16 @@
}
},
"HelloWorld": {
"digest": "11c21044c0ab297f29b386d1d04d0318cff4effa75191371dd576282882c2a8d",
"digest": "103e8bd9af45de70a1b214377e71bd05ae464e932dbe48aeada70362fa38b05",
"methods": {
"update": {
"rows": 826,
"digest": "5215ab9c7474b26f3a9073d821aef59b"
"rows": 825,
"digest": "226ff56f432f39d8056bf7f1813669f5"
}
},
"verificationKey": {
"data": "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",
"hash": "12073693408844675717690269959590209616934536940765116494766700428390466839"
"data": "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",
"hash": "21868179330353774930881761197110482602042218337984430556708224781269131363222"
}
},
"TokenContract": {
Expand Down Expand Up @@ -251,7 +251,7 @@
}
},
"diverse": {
"digest": "3822c67734f73c594779eab98899d2e4cc254b7b9d5261b2aada529f781c1234",
"digest": "384e7eb44f1ad3b899aae057c48a9eab8ad72c8120ef13a1e906c91b93028828",
"methods": {
"ecdsa": {
"rows": 28186,
Expand All @@ -262,8 +262,8 @@
"digest": "1496e5b8cc253c9909551a25c190ab85"
},
"pallas": {
"rows": 1616,
"digest": "8a9ee7267139d6280971f9750a610713"
"rows": 1610,
"digest": "0ff5537aae21b4d47b8c901aa5e71007"
},
"poseidon": {
"rows": 947,
Expand All @@ -279,8 +279,8 @@
}
},
"verificationKey": {
"data": "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",
"hash": "16059383130523018273678724949684090892300714372032191113254352030711948582951"
"data": "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",
"hash": "25099125262785569464008872277824954042674175732318382315709475361977342159172"
}
}
}

0 comments on commit 72b4d12

Please sign in to comment.