|
| 1 | +import { Abi, WitnessMap } from '@noir-lang/types'; |
| 2 | + |
| 3 | +export function flattenPublicInputs(publicInputs: WitnessMap): string[] { |
| 4 | + const publicInputIndices = [...publicInputs.keys()].sort(); |
| 5 | + const flattenedPublicInputs = publicInputIndices.map((index) => publicInputs.get(index) as string); |
| 6 | + return flattenedPublicInputs; |
| 7 | +} |
| 8 | + |
| 9 | +export function flattenPublicInputsAsArray(publicInputs: WitnessMap): Uint8Array { |
| 10 | + const flatPublicInputs = flattenPublicInputs(publicInputs); |
| 11 | + const flattenedPublicInputs = flatPublicInputs.map(hexToUint8Array); |
| 12 | + return flattenUint8Arrays(flattenedPublicInputs); |
| 13 | +} |
| 14 | + |
| 15 | +export function deflattenPublicInputs(flattenedPublicInputs: Uint8Array, abi: Abi): WitnessMap { |
| 16 | + const publicInputSize = 32; |
| 17 | + const chunkedFlattenedPublicInputs: Uint8Array[] = []; |
| 18 | + |
| 19 | + for (let i = 0; i < flattenedPublicInputs.length; i += publicInputSize) { |
| 20 | + const publicInput = flattenedPublicInputs.slice(i, i + publicInputSize); |
| 21 | + chunkedFlattenedPublicInputs.push(publicInput); |
| 22 | + } |
| 23 | + |
| 24 | + const return_value_witnesses = abi.return_witnesses; |
| 25 | + const public_parameters = abi.parameters.filter((param) => param.visibility === 'public'); |
| 26 | + const public_parameter_witnesses: number[] = public_parameters.flatMap((param) => |
| 27 | + abi.param_witnesses[param.name].flatMap((witness_range) => |
| 28 | + Array.from({ length: witness_range.end - witness_range.start }, (_, i) => witness_range.start + i), |
| 29 | + ), |
| 30 | + ); |
| 31 | + |
| 32 | + // We now have an array of witness indices which have been deduplicated and sorted in ascending order. |
| 33 | + // The elements of this array should correspond to the elements of `flattenedPublicInputs` so that we can build up a `WitnessMap`. |
| 34 | + const public_input_witnesses = [...new Set(public_parameter_witnesses.concat(return_value_witnesses))].sort(); |
| 35 | + |
| 36 | + const publicInputs: WitnessMap = new Map(); |
| 37 | + public_input_witnesses.forEach((witness_index, index) => { |
| 38 | + const witness_value = uint8ArrayToHex(chunkedFlattenedPublicInputs[index]); |
| 39 | + publicInputs.set(witness_index, witness_value); |
| 40 | + }); |
| 41 | + |
| 42 | + return publicInputs; |
| 43 | +} |
| 44 | + |
| 45 | +function flattenUint8Arrays(arrays: Uint8Array[]): Uint8Array { |
| 46 | + const totalLength = arrays.reduce((acc, val) => acc + val.length, 0); |
| 47 | + const result = new Uint8Array(totalLength); |
| 48 | + |
| 49 | + let offset = 0; |
| 50 | + for (const arr of arrays) { |
| 51 | + result.set(arr, offset); |
| 52 | + offset += arr.length; |
| 53 | + } |
| 54 | + |
| 55 | + return result; |
| 56 | +} |
| 57 | + |
| 58 | +function uint8ArrayToHex(buffer: Uint8Array): string { |
| 59 | + const hex: string[] = []; |
| 60 | + |
| 61 | + buffer.forEach(function (i) { |
| 62 | + let h = i.toString(16); |
| 63 | + if (h.length % 2) { |
| 64 | + h = '0' + h; |
| 65 | + } |
| 66 | + hex.push(h); |
| 67 | + }); |
| 68 | + |
| 69 | + return '0x' + hex.join(''); |
| 70 | +} |
| 71 | + |
| 72 | +function hexToUint8Array(hex: string): Uint8Array { |
| 73 | + const sanitised_hex = BigInt(hex).toString(16).padStart(64, '0'); |
| 74 | + |
| 75 | + const len = sanitised_hex.length / 2; |
| 76 | + const u8 = new Uint8Array(len); |
| 77 | + |
| 78 | + let i = 0; |
| 79 | + let j = 0; |
| 80 | + while (i < len) { |
| 81 | + u8[i] = parseInt(sanitised_hex.slice(j, j + 2), 16); |
| 82 | + i += 1; |
| 83 | + j += 2; |
| 84 | + } |
| 85 | + |
| 86 | + return u8; |
| 87 | +} |
0 commit comments