diff --git a/src/lib/provable/int.ts b/src/lib/provable/int.ts index 15c64c6249..e26c2cca07 100644 --- a/src/lib/provable/int.ts +++ b/src/lib/provable/int.ts @@ -18,6 +18,7 @@ import { lessThanOrEqualGeneric, } from './gadgets/comparison.js'; import { assert } from '../util/assert.js'; +import { TupleN } from '../util/types.js'; // external API export { UInt8, UInt32, UInt64, Int64, Sign }; @@ -956,6 +957,47 @@ class UInt32 extends CircuitValue { ): InstanceType { return UInt32.from(x) as any; } + + /** + * Split a UInt32 into 4 UInt8s, in little-endian order. + */ + toBytes(): TupleN { + // witness the bytes + let bytes = Provable.witness(Provable.Array(UInt8, 4), () => { + let x = this.value.toBigInt(); + return [0, 1, 2, 3].map((i) => UInt8.from((x >> BigInt(8 * i)) & 0xffn)); + }); + // prove that bytes are correct + UInt32.fromBytes(bytes).assertEquals(this); + return TupleN.fromArray(4, bytes); + } + + /** + * Split a UInt32 into 4 UInt8s, in big-endian order. + */ + toBytesBE(): TupleN { + return TupleN.fromArray(4, this.toBytes().reverse()); + } + + /** + * Combine 4 UInt8s into a UInt32, in little-endian order. + */ + static fromBytes(bytes: UInt8[]): UInt32 { + assert(bytes.length === 4, '4 bytes needed to create a uint32'); + + let word = Field(0); + bytes.forEach(({ value }, i) => { + word = word.add(value.mul(1n << BigInt(8 * i))); + }); + return UInt32.Unsafe.fromField(word); + } + + /** + * Combine 4 UInt8s into a UInt32, in big-endian order. + */ + static fromBytesBE(bytes: UInt8[]): UInt32 { + return UInt32.fromBytes([...bytes].reverse()); + } } class Sign extends CircuitValue {