import { randomBytes } from 'crypto' import { readFileSync } from 'fs'; import { Fr } from '@aztec/bb.js/dest/types/index.js'; import { MerkleTree } from './MerkleTree.mjs'; import { keccak256 } from "@ethersproject/keccak256/lib/index.js"; import fs from 'fs' export function randomBytesFr(numBytes) { const bytes = randomBytes(numBytes) const bytesFr = Fr.fromBufferReduce(bytes) return bytesFr } export const format = (data) => { if (data.length === 0) return "[]" return `[\n "${data.join('",\n "')}"\n]`; } export const dumpToml = (data) => { data = ` tx_in = ${format(data.tx_in)} amount_public_in = "${data.amount_public_in}" amount_public_out = "${data.amount_public_out}" commitment_out = ${format(data.commitment_out)} recipient = "${data.recipient}" oracle = "${data.oracle}" old_root = "${data.old_root}" new_root = "${data.new_root}" secrets = ${format(data.secrets)} utxo_in = ${format(data.utxo_in)} utxo_out = ${format(data.utxo_out)} roots = ${format(data.roots)} leaves = ${format(data.leaves)} indexes = ${format(data.indexes)} hash_path = ${format(data.hash_path)} nullifier_hashes = ${format(data.nullifiers)} ` fs.writeFileSync('./Prover.toml', data); } export function path_to_uint8array(path) { let buffer = readFileSync(path); return new Uint8Array(buffer); } const toFixedHex = (number, pad0x, length = 32) => { let hexString = number.toString(16).padStart(length * 2, '0'); return (pad0x ? `0x` + hexString : hexString); } export function getSolidityHash(asset) { // Flatten the object asset = 0; return keccak256(asset); } export function generateHashPathInput(hash_path) { let hash_path_input = []; for (var i = 0; i < hash_path.length; i++) { hash_path_input.push(`0x` + hash_path[i]); } return hash_path_input; } export function generateUTXO(batchSize, amounts, _secrets, BarretenbergApi) { let utxos = [] for (let i = 0; i < batchSize; i++) { let amountBN = amounts[i] let utxo = { secret: _secrets[i], owner: BarretenbergApi.pedersenPlookupCompress([_secrets[i]]), amountBN: amountBN, amount: Fr.fromString(toFixedHex(Number(amountBN.toString()), true)), assetType: Fr.fromBufferReduce(Buffer.from(getSolidityHash(0), 'hex')), } utxos.push(utxo) } return utxos } export function generateTreeProof(utxoIn, trees, BarretenbergApi) { let treeProof = [] for (let i = 0; i < utxoIn.length; i++) { let commitment = BarretenbergApi.pedersenPlookupCompress( [utxoIn[i].owner, utxoIn[i].amount, utxoIn[i].assetType] ).toString() let proofs = { utxo: { leaf: commitment, index: trees.utxoTreeOld.getIndex(commitment), root: trees.utxoTreeOld.root(), hash_path: trees.utxoTreeOld.proof( trees.utxoTreeOld.getIndex(commitment) ).pathElements }, tx: { leaf: trees.utxoTreeOld.root(), index: trees.txTreeOld.getIndex(trees.utxoTreeOld.root()), root: trees.txTreeOld.root(), hash_path: trees.txTreeOld.proof( trees.txTreeOld.getIndex(trees.utxoTreeOld.root()) ).pathElements }, batch: { leaf: trees.batchLeaf, index: trees.batch_tree.getIndex(trees.batchLeaf), root: trees.batch_tree.root(), hash_path: trees.batch_tree.proof( trees.batch_tree.getIndex(trees.batchLeaf) ).pathElements }, historic: { leaf: trees.newHistoricRoot, index: trees.historic_tree.getIndex(trees.newHistoricRoot), root: trees.historic_tree.root(), hash_path: trees.historic_tree.proof( trees.historic_tree.getIndex(trees.newHistoricRoot) ).pathElements } } treeProof.push(proofs) } return treeProof } export function generateDataToml(oldRoot, newRoot, api) { let zeroHash = api.pedersenPlookupCompress([Fr.fromString(toFixedHex(0, true))]) const data = { tx_in: new Array(16).fill('0xf35fcb490b7ea67c3ac26ed530fa5d8dfe8be344e7177ebb63fe02723fb6f725'), secrets: new Array(16).fill('0'), utxo_in: new Array(48).fill('0'), utxo_out: new Array(48).fill('0'), oracle: zeroHash.toString(), old_root: "0xf35fcb490b7ea67c3ac26ed530fa5d8dfe8be344e7177ebb63fe02723fb6f725", new_root: "0xf35fcb490b7ea67c3ac26ed530fa5d8dfe8be344e7177ebb63fe02723fb6f725", roots: new Array(64).fill('0'), leaves: new Array(64).fill('0'), indexes: new Array(64).fill('0'), hash_path: new Array(288).fill('0'), commitment_out: new Array(16).fill('0xf35fcb490b7ea67c3ac26ed530fa5d8dfe8be344e7177ebb63fe02723fb6f725'), amount_public_in: "0", amount_public_out: "0", nullifiers: new Array(16).fill('0'), recipient: Fr.fromString(toFixedHex(0, true)).toString() } if (oldRoot !== "0") data.old_root = oldRoot; if (newRoot !== "0") data.new_root = newRoot; return data } export function generateTestTransaction(utxoIn, utxoOut, trees, treeProof, amountPublic, data, recipient, BarretenbergApi) { let utxoInLen = utxoIn.length let utxoOutLen = utxoOut.length // UTXOs being spent for (let i = 0; i < utxoInLen; i++) { let ownerHex = utxoIn[i].owner.toString(); let amountHex = utxoIn[i].amount.toString(); let assetTypeHex = utxoIn[i].assetType.toString(); let note_commitment = BarretenbergApi.pedersenPlookupCompress([utxoIn[i].owner, utxoIn[i].amount, utxoIn[i].assetType]); let utxoLeaf = note_commitment.toString() data.secrets[i] = utxoIn[i].secret data.nullifiers[i] = BarretenbergApi.pedersenPlookupCompressFields(utxoIn[i].secret, utxoIn[i].secret) data.utxo_in[i*3 + 0] = ownerHex data.utxo_in[i*3 + 1] = amountHex data.utxo_in[i*3 + 2] = assetTypeHex data.leaves[i*4 + 0] = utxoLeaf data.leaves[i*4 + 1] = treeProof[i].tx.leaf data.leaves[i*4 + 2] = treeProof[i].batch.leaf data.leaves[i*4 + 3] = treeProof[i].historic.leaf data.indexes[i*4 + 0] = treeProof[i].utxo.index data.indexes[i*4 + 1] = treeProof[i].tx.index data.indexes[i*4 + 2] = treeProof[i].batch.index data.indexes[i*4 + 3] = treeProof[i].historic.index data.roots[i*4 + 0] = treeProof[i].utxo.root data.roots[i*4 + 1] = treeProof[i].tx.root data.roots[i*4 + 2] = treeProof[i].batch.root data.roots[i*4 + 3] = treeProof[i].historic.root let utxoPath = treeProof[i].utxo.hash_path let txPath = treeProof[i].tx.hash_path let batchPath = treeProof[i].batch.hash_path let historicPath = treeProof[i].historic.hash_path for (let j = 0; j < utxoPath.length; j++) { data.hash_path[i*18 + 0 + j] = utxoPath[j] data.hash_path[i*18 + 4 + j] = txPath[j] } for (let k = 0; k < batchPath.length; k++) { data.hash_path[i*18 + 8 + k] = batchPath[k] data.hash_path[i*18 + 13 + k] = historicPath[k] } } // UTXOs being generated for (let i = 0; i < utxoOutLen; i++) { let ownerHex = utxoOut[i].owner.toString(); let amountHex = utxoOut[i].amount.toString(); let assetTypeHex = utxoOut[i].assetType.toString(); let note_commitment = BarretenbergApi.pedersenPlookupCompress([utxoOut[i].owner, utxoOut[i].amount, utxoOut[i].assetType]); let utxoLeaf = note_commitment.toString() trees.utxo_tree.insert(utxoLeaf) data.utxo_out[i*3 + 0] = ownerHex data.utxo_out[i*3 + 1] = amountHex data.utxo_out[i*3 + 2] = assetTypeHex data.commitment_out[i] = utxoLeaf } data.tx_in[0] = trees.utxo_tree.root() data.amount_public_in = toFixedHex(Number(amountPublic.amountIn.toString()), true) data.amount_public_out = toFixedHex(Number(amountPublic.amountOut.toString()), true) data.recipient = recipient dumpToml(data) } export function generateTestPublish(trees, data, api) { // Publish our local set of test txs let utxoTree = trees.utxo_tree let txTree = trees.tx_tree let batchTree = trees.batch_tree let historicTree = trees.historic_tree let utxoRoot = utxoTree.root() txTree.insert(utxoRoot) let txRoot = txTree.root() let txRootFr = Fr.fromBufferReduce(Buffer.from(txRoot.slice(2), 'hex')) let oracleFr = Fr.fromBufferReduce(toFixedHex(0, true)) let oracleHash = api.pedersenPlookupCompress([oracleFr]) let batch = api.pedersenPlookupCompressFields(txRootFr, oracleHash) let batchHex = batch.toString() batchTree.insert(batchHex) let oldHistoricRoot = Fr.fromBufferReduce(Buffer.from(data.new_root.slice(2), 'hex')) let newHistoricRoot = api.pedersenPlookupCompress([batch, oldHistoricRoot]) let newHistoricRootHex = newHistoricRoot.toString() historicTree.insert(newHistoricRootHex) data.old_root = oldHistoricRoot.toString() data.new_root = newHistoricRootHex // Clearing the utxo tree for the next batch // Saving the old tree for proofs trees.utxoTreeOld = trees.utxo_tree trees.txTreeOld = trees.tx_tree trees.batchLeaf = batchHex trees.newHistoricRoot = newHistoricRootHex trees.utxo_tree = new MerkleTree(4, api) trees.tx_tree = new MerkleTree(4, api) dumpToml(data) }