UNPKG

mina-attestations

Version:
117 lines (116 loc) 5.72 kB
import { Bytes, Provable, UInt32, UInt64 } from 'o1js'; import { deepStrictEqual } from 'node:assert'; import { DynamicSHA2 } from "./dynamic-sha2.js"; import { stringLength, zip } from "../util.js"; import { DynamicBytes } from "./dynamic-bytes.js"; import test from 'node:test'; import { SHA2 } from "./sha2.js"; import { DynamicString } from "./dynamic-string.js"; const DynBytes = DynamicBytes({ maxLength: 430 }); const StaticBytes = Bytes(stringLength(longString())); let bytes = DynBytes.fromString(longString()); let staticBytes = StaticBytes.fromString(longString()); await test('padding 256', () => { let actualPadding = DynamicSHA2.padding256(bytes); let expectedPadding = SHA2.padding256(staticBytes); deepStrictEqual(actualPadding.toValue().map(blockToHexBytes), expectedPadding.map(blockToHexBytes)); }); await test('padding 512', () => { let actualPadding = DynamicSHA2.padding512(bytes); let expectedPadding = SHA2.padding512(staticBytes); deepStrictEqual(actualPadding.toValue().map(blockToHexBytes64), expectedPadding.map(blockToHexBytes64)); }); const expectedHash256 = await sha2(256, longString()); const expectedHash384 = await sha2(384, longString()); const expectedHash512 = await sha2(512, longString()); await test('sha256 outside circuit', async () => { deepStrictEqual(DynamicSHA2.hash(256, bytes).toBytes(), expectedHash256.toBytes()); deepStrictEqual(SHA2.hash(256, staticBytes).toBytes(), expectedHash256.toBytes()); // also works with DynamicString and DynamicBytes let String = DynamicString({ maxLength: 20 }); let string = String.from('hello'); deepStrictEqual(string.hashToBytes('sha2-256').toHex(), (await sha2(256, 'hello')).toHex()); let Bytes = DynamicBytes({ maxLength: 20 }); let bytes_ = Bytes.fromString('hello again!'); deepStrictEqual(bytes_.hashToBytes('sha2-256').toHex(), (await sha2(256, 'hello again!')).toHex()); }); await test('sha384 outside circuit', () => { deepStrictEqual(DynamicSHA2.hash(384, bytes).toBytes(), expectedHash384.toBytes()); deepStrictEqual(SHA2.hash(384, staticBytes).toBytes(), expectedHash384.toBytes()); }); await test('sha512 outside circuit', () => { deepStrictEqual(DynamicSHA2.hash(512, bytes).toBytes(), expectedHash512.toBytes()); deepStrictEqual(SHA2.hash(512, staticBytes).toBytes(), expectedHash512.toBytes()); }); // in-circuit test async function circuit(len) { let bytesVar = Provable.witness(DynBytes, () => bytes); let hash = DynamicSHA2.hash(len, bytesVar); zip(hash.bytes, (await sha2(len, longString())).bytes).forEach(([a, b], i) => { a.assertEquals(b, `hash[${i}]`); }); } async function circuitStatic(len) { let bytesVar = Provable.witness(StaticBytes, () => staticBytes); let hash = SHA2.hash(len, bytesVar); zip(hash.bytes, (await sha2(len, longString())).bytes).forEach(([a, b], i) => { a.assertEquals(b, `hash[${i}]`); }); } await test('sha256 inside circuit', async () => { await Provable.runAndCheck(() => circuit(256)); await Provable.runAndCheck(() => circuitStatic(256)); }); await test('sha384 inside circuit', async () => { await Provable.runAndCheck(() => circuit(384)); await Provable.runAndCheck(() => circuitStatic(384)); }); await test('sha512 inside circuit', async () => { await Provable.runAndCheck(() => circuit(512)); await Provable.runAndCheck(() => circuitStatic(512)); }); // constraints async function checkConstraints(len) { let constraints = await Provable.constraintSystem(() => circuit(len)); console.log(`\nsha2 ${len} constraints`); console.log('dynamic', constraints.rows); let constraintStatic = await Provable.constraintSystem(() => circuitStatic(len)); console.log('static', constraintStatic.rows); let staticPadding = len === 256 ? SHA2.padding256(staticBytes) : SHA2.padding512(staticBytes); let dynamicPadding = len === 256 ? DynamicSHA2.padding256(bytes) : DynamicSHA2.padding512(bytes); let ratio = constraints.rows / constraintStatic.rows; console.log(`static # of bytes: ${staticBytes.length}`); console.log(`max dynamic # of bytes: ${bytes.maxLength}`); console.log(`static # of blocks: ${staticPadding.length}`); console.log(`max dynamic # of blocks: ${dynamicPadding.maxLength}`); console.log(`constraint overhead for dynamic: ${((ratio - 1) * 100).toFixed(2)}%`); } await test('constraints dynamic vs static (256)', () => checkConstraints(256)); await test('constraints dynamic vs static (512)', () => checkConstraints(512)); // reference implementation using the Web Crypto API async function sha2(len, input) { let buffer = await crypto.subtle.digest(`SHA-${len}`, new TextEncoder().encode(input)); return Bytes(len / 8).from(new Uint8Array(buffer)); } // helpers function toHexBytes(uint32) { return UInt32.from(uint32).toBigint().toString(16).padStart(8, '0'); } function blockToHexBytes(block) { return block.map(toHexBytes); } function toHexBytes64(uint32) { return UInt64.from(uint32).toBigInt().toString(16).padStart(16, '0'); } function blockToHexBytes64(block) { return block.map(toHexBytes64); } // ~400 bytes, needs 7 blocks, also contains unicode function longString() { return `SHA-2 (Secure Hash Algorithm 2) is a set of cryptographic hash functions designed by the United States National Security Agency (NSA) and first published in 2001.[3][4] They are built using the Merkle–Damgård construction, from a one-way compression function itself built using the Davies–Meyer structure from a specialized block cipher. SHA-2 includes significant changes from its predecessor, SHA-1.`; } //# sourceMappingURL=dynamic-sha2.test.js.map