Created
September 5, 2024 18:26
-
-
Save brendanw/1a984020b1f233c5210ab2f56c8dd752 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Function to generate AccountSalt | |
import { buildPoseidon } from 'circomlibjs'; | |
/** | |
* Attempt at reproducing the account salt generation that zkemail does on the backend at -> | |
* https://github.com/zkemail/relayer-utils/blob/aefb00fdcfbfde7fced50e5a0b086e5cc2ff64cd/src/cryptos.rs#L198 | |
* @param email | |
* @param accountCode | |
*/ | |
export async function generateAccountSalt(email: string, accountCode: bigint): Promise<string> { | |
const maxEmailBytes = 256; | |
// 1. Pad the email address | |
const paddedEmailBytes = padEmailAddr(email, maxEmailBytes); | |
// 2. Convert padded email to Poseidon field elements | |
const emailFields = bytesToFields(paddedEmailBytes); | |
// 3. Poseidon hash function | |
const poseidon = await buildPoseidon(); | |
// 4. Prepare inputs for Poseidon hashing | |
const inputs = [...emailFields, BigInt(accountCode), BigInt(0)]; | |
// 5. Compute Poseidon hash to get the AccountSalt | |
const accountSalt = poseidon(inputs); | |
return uint8ArrayToHex(accountSalt); | |
} | |
function bytesToFields(bytes: Uint8Array): bigint[] { | |
const chunkSize = 31; | |
const fieldElements: bigint[] = []; | |
// Process the bytes in chunks of 31 bytes | |
for (let i = 0; i < bytes.length; i += chunkSize) { | |
// Get the current chunk (31 bytes) | |
const chunk = bytes.slice(i, i + chunkSize); | |
// Extend the chunk to 32 bytes by padding with zeros | |
const extended = new Uint8Array(32); | |
extended.set(chunk); // Copy chunk into the first part of the 32-byte array | |
// Convert the 32-byte array to a BigInt or field element | |
const fieldElement = uint8ArrayToBigInt(extended); // Assuming we can treat it as a number here | |
fieldElements.push(fieldElement); | |
} | |
return fieldElements; | |
} | |
function uint8ArrayToBigInt(uint8Array: Uint8Array): bigint { | |
let result = 0n; // Start with a bigint value of 0 | |
for (const byte of uint8Array) { | |
// eslint-disable-next-line no-bitwise | |
result = (result << 8n) + BigInt(byte); // Shift the current value by 8 bits (1 byte) and add the next byte | |
} | |
return result; | |
} | |
function uint8ArrayToHex(uint8Array: Uint8Array): string { | |
const noPrepend = Array.from(uint8Array) | |
.map((byte) => byte.toString(16).padStart(2, '0')) // Convert each byte to hex and pad with '0' if necessary | |
.join(''); // Join all hex strings into one | |
return `0x${noPrepend}`; | |
} | |
function padEmailAddr(email: string, maxBytes: number): Uint8Array { | |
const emailBytes = new TextEncoder().encode(email); | |
const paddedBytes = new Uint8Array(maxBytes); | |
paddedBytes.set(emailBytes); | |
return paddedBytes; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment