Skip to main content

Overview

Utility functions provide cryptographic primitives, encoding helpers, and data manipulation tools used throughout the Tornado Nova system. Source Locations: src/utils.js, src/utxo.js, src/keypair.js

Cryptographic Functions

poseidonHash

function poseidonHash(items)
Computes Poseidon hash of an array of field elements. Poseidon is a ZK-friendly hash function.
items
Array<BigNumber | number>
required
Array of values to hash
Returns: BigNumber - Hash result as a field element Source: src/utils.js:7
const { poseidonHash } = require('./src/utils')
const { ethers } = require('hardhat')

const hash = poseidonHash([1, 2, 3])
console.log(hash.toString())

// Hash commitment
const commitment = poseidonHash([amount, pubkey, blinding])

poseidonHash2

function poseidonHash2(a, b)
Computes Poseidon hash of exactly two field elements. Optimized for merkle tree operations.
a
BigNumber | number
required
First value
b
BigNumber | number
required
Second value
Returns: BigNumber - Hash result Source: src/utils.js:8
const { poseidonHash2 } = require('./src/utils')

// Used in merkle tree
const parent = poseidonHash2(leftChild, rightChild)

randomBN

function randomBN(nbytes = 31)
Generates a cryptographically secure random BigNumber.
nbytes
number
default:"31"
Number of random bytes to generate (default 31 to stay within field size)
Returns: BigNumber - Random number Source: src/utils.js:15
const { randomBN } = require('./src/utils')

// Generate random blinding factor
const blinding = randomBN()

// Generate smaller random number
const randomValue = randomBN(16) // 16 bytes

Encoding Functions

toFixedHex

function toFixedHex(number, length = 32)
Converts a number or Buffer to a fixed-length hex string with ‘0x’ prefix.
number
BigNumber | number | Buffer
required
Value to convert
length
number
default:"32"
Byte length of output (will be padded with zeros)
Returns: string - Hex string of format 0x... with exactly length * 2 hex digits Source: src/utils.js:51
const { toFixedHex } = require('./src/utils')
const { ethers } = require('hardhat')

// Convert address to fixed hex (20 bytes)
const addressHex = toFixedHex(recipientAddress, 20)
// "0x000000000000000000000000742d35cc6634c0532925a3b844bc9e7595f0beb"

// Convert number to 32 bytes
const amountHex = toFixedHex(ethers.utils.parseEther('1.0'))
// "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"

// Handle negative numbers
const negativeHex = toFixedHex(-100)
// Properly encodes as field element

toBuffer

function toBuffer(value, length)
Converts a value to a Buffer of specified byte length.
value
BigNumber | number
required
Value to convert
length
number
required
Byte length of output buffer
Returns: Buffer - Buffer padded to specified length Source: src/utils.js:65
const { toBuffer } = require('./src/utils')

// Convert amount to 31 bytes
const amountBuf = toBuffer(amount, 31)

// Concatenate multiple values
const combined = Buffer.concat([
  toBuffer(amount, 31),
  toBuffer(blinding, 31)
])

getExtDataHash

function getExtDataHash({
  recipient,
  extAmount,
  relayer,
  fee,
  encryptedOutput1,
  encryptedOutput2,
  isL1Withdrawal,
  l1Fee
})
Computes the hash of external data structure for ZK proof verification.
recipient
string | BigNumber
required
Recipient address
extAmount
string | BigNumber
required
External amount
relayer
string | BigNumber
required
Relayer address
fee
string | BigNumber
required
Fee amount
encryptedOutput1
bytes
required
First encrypted output
encryptedOutput2
bytes
required
Second encrypted output
isL1Withdrawal
boolean
required
L1 withdrawal flag
l1Fee
BigNumber
required
L1 fee amount
Returns: BigNumber - Hash modulo FIELD_SIZE Source: src/utils.js:17
const { getExtDataHash, toFixedHex } = require('./src/utils')

const extData = {
  recipient: toFixedHex(recipientAddress, 20),
  extAmount: toFixedHex(amount),
  relayer: toFixedHex(relayerAddress, 20),
  fee: toFixedHex(feeAmount),
  encryptedOutput1: output1.encrypt(),
  encryptedOutput2: output2.encrypt(),
  isL1Withdrawal: false,
  l1Fee: 0
}

const extDataHash = getExtDataHash(extData)
// Used in ZK proof

shuffle

function shuffle(array)
Randomly shuffles an array in-place using Fisher-Yates algorithm.
array
Array
required
Array to shuffle (modified in-place)
Returns: Array - The shuffled array (same reference) Source: src/utils.js:74
const { shuffle } = require('./src/utils')

// Shuffle inputs for privacy
const inputs = [utxo1, utxo2, utxo3]
shuffle(inputs)

// Shuffle outputs
const outputs = [outputUtxo1, outputUtxo2]
shuffle(outputs)

// Array is modified in-place
console.log(inputs) // Randomly ordered

Constants

FIELD_SIZE

const FIELD_SIZE = BigNumber.from(
  '21888242871839275222246405745257275088548364400416034343698204186575808495617'
)
The field size for BN254 elliptic curve used in the ZK-SNARKs. All field elements must be less than this value. Source: src/utils.js:10
const { FIELD_SIZE } = require('./src/utils')

// Ensure value is within field
const fieldElement = value.mod(FIELD_SIZE)

// Calculate public amount
const publicAmount = BigNumber.from(extAmount)
  .sub(fee)
  .add(FIELD_SIZE)
  .mod(FIELD_SIZE)

Utility Classes

Keypair

See the Types Reference for full Keypair class documentation. Key Methods:
  • constructor(privkey?) - Create new keypair
  • toString() - Export as string
  • Keypair.fromString(str) - Import from string
  • sign(commitment, merklePath) - Sign for nullifier
  • encrypt(bytes) - Encrypt data
  • decrypt(data) - Decrypt data
Source: src/keypair.js:36

Utxo

See the Types Reference for full Utxo class documentation. Key Methods:
  • constructor({ amount, keypair, blinding, index }) - Create UTXO
  • getCommitment() - Get commitment hash
  • getNullifier() - Get nullifier hash
  • encrypt() - Encrypt UTXO data
  • Utxo.decrypt(keypair, data, index) - Decrypt UTXO
Source: src/utxo.js:6

Encryption Helpers

packEncryptedMessage

function packEncryptedMessage(encryptedMessage)
Packs encrypted message object into hex string format.
encryptedMessage
object
required
Encrypted message object with nonce, ephemPublicKey, ciphertext (base64)
Returns: string - Hex string with ‘0x’ prefix Format: 24 bytes nonce + 32 bytes ephemeral public key + ciphertext Source: src/keypair.js:6

unpackEncryptedMessage

function unpackEncryptedMessage(encryptedMessage)
Unpacks hex string into encrypted message object.
encryptedMessage
string
required
Hex string (with or without ‘0x’ prefix)
Returns: object - Object with version, nonce, ephemPublicKey, ciphertext (all base64) Source: src/keypair.js:20

Testing Utilities

getSignerFromAddress

async function getSignerFromAddress(address)
Impersonates an address in Hardhat for testing. Only works in local Hardhat network.
address
string
required
Address to impersonate
Returns: Promise<Signer> - Ethers.js signer for the address Source: src/utils.js:91
const { getSignerFromAddress } = require('./src/utils')

// Impersonate the multisig
const multisigSigner = await getSignerFromAddress(multisigAddress)

// Call restricted function
const tornadoPool = await ethers.getContractAt(
  'TornadoPool',
  poolAddress,
  multisigSigner
)

await tornadoPool.configureLimits(newLimit)

Usage Examples

const { 
  toFixedHex, 
  getExtDataHash, 
  poseidonHash,
  randomBN,
  FIELD_SIZE 
} = require('./src/utils')
const { Keypair } = require('./src/keypair')
const Utxo = require('./src/utxo')

// Create keypair
const keypair = new Keypair()

// Create UTXO
const utxo = new Utxo({
  amount: ethers.utils.parseEther('1.0'),
  keypair,
  blinding: randomBN()
})

// Get commitment
const commitment = utxo.getCommitment()
console.log(`Commitment: ${toFixedHex(commitment)}`)

// Encrypt for recipient
const encrypted = utxo.encrypt()

// Create external data
const extData = {
  recipient: toFixedHex(recipientAddress, 20),
  extAmount: toFixedHex(utxo.amount),
  relayer: toFixedHex(0, 20),
  fee: toFixedHex(0),
  encryptedOutput1: encrypted,
  encryptedOutput2: new Utxo().encrypt(),
  isL1Withdrawal: false,
  l1Fee: 0
}

// Hash external data
const extDataHash = getExtDataHash(extData)
console.log(`ExtData hash: ${toFixedHex(extDataHash)}`)
Field Elements: All cryptographic operations work with field elements modulo FIELD_SIZE. Negative numbers are represented as FIELD_SIZE - abs(value).
Random Number Generation: Always use randomBN() for generating blinding factors and other cryptographic secrets. Never use Math.random() or predictable values.

Build docs developers (and LLMs) love