Skip to content

Instantly share code, notes, and snippets.

@brendanw
Created September 5, 2024 18:26
Show Gist options
  • Save brendanw/1a984020b1f233c5210ab2f56c8dd752 to your computer and use it in GitHub Desktop.
Save brendanw/1a984020b1f233c5210ab2f56c8dd752 to your computer and use it in GitHub Desktop.
// 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