"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TransactionBuilder = void 0; const rover_1 = require("./rover"); const interfaces = __importStar(require("./interfaces")); class TransactionBuilder extends rover_1.Prover { constructor() { super(...arguments); this.zeroValue = "0x016a430aa58685aba1311244a973a3bc358859da86784be51094368e8fb6f720"; this.zeroHex = "0x0000000000000000000000000000000000000000000000000000000000000000"; this.withdrawals = []; this.utxoproofs = []; this.utxos = []; this.depositTotal = "0"; this.contractInputs = []; this.getOracle = async (old_root) => await this.pedersen(this.toFixedHex(0, true)); this.fetchSiblings = async (utxo) => { const utxoId = await this.pedersen([utxo.owner, utxo.amount, utxo.assetType]); const merkleRoot = await this.momiji.contract.getRootFromUtxo(utxoId); const filter = this.momiji.contract.filters.TransactionSent(null, merkleRoot, null); const txEvents = await this.momiji.contract.queryFilter(filter); let siblings = []; for (let i = 0; i < txEvents.length; i++) { let commitments = []; let event = txEvents[i]; let args = event.topics; for (let j = 19; j < 35; j++) { if (args[0][j] !== this.toFixedHex(0, true)) commitments.push(args[0][j]); else commitments.push("0x016a430aa58685aba1311244a973a3bc358859da86784be51094368e8fb6f720"); } if (commitments.includes(utxoId)) siblings = commitments; } return siblings; }; this.makeUTXOTree = async (utxo) => { if (!utxo) utxo = this.utxos; let commitments = []; for (let i = 0; i < utxo.length; i++) commitments[i] = await this.pedersen([utxo[i].owner, utxo[i].amount, utxo[i].assetType]); let utxoTree = new interfaces.MerkleTree(this.config.tree.utxoDepth, this.momiji.api, commitments); await utxoTree.init(); return utxoTree; }; this.proveTransaction = async (transaction, public_inputs) => { const _contract = new interfaces.Contract(this.config.stateContract.address, JSON.stringify(this.config.stateContract.abi), this.signer); let txWitness = await this.momiji.transaction.final.execute(transaction); let txProof = await this.momiji.transaction.intermediate.generateIntermediateProof(txWitness.witness); let txProofArtifacts = await this.momiji.transaction.intermediate.generateIntermediateProofArtifacts(txProof, 1); const pi = public_inputs; const _contract_inputs = [ pi.current_root, pi.deposit, pi.tx_id, pi.withdrawals, pi.commitment_out, pi.recipients, pi.nullifier_hashes, txProofArtifacts.proofAsFields, transaction.pi_contract_hash ].flat(); _contract.enqueue.send(_contract_inputs).then((_tx) => console.log(_tx), (_r) => console.log(_r)); return { proof: txProofArtifacts.proofAsFields, pi_contract_hash: transaction.pi_contract_hash, ...public_inputs }; }; this.hashPublicInputs = (public_inputs) => { const inputsForHashing = [ "0x", public_inputs.current_root.slice(2), public_inputs.deposit.slice(2), public_inputs.tx_id.slice(2), [...public_inputs.withdrawals.map(w => w.slice(2))].join(''), [...public_inputs.commitment_out.map(w => w.slice(2))].join(''), [...public_inputs.recipients.map(w => w.slice(2))].join(''), [...public_inputs.nullifier_hashes.map(w => w.slice(2))].join(''), ].join(''); return this.keccak256(inputsForHashing); }; this.deposit = (amount) => this.depositTotal = this.toFixedHex(amount, true); this.send = async (sends) => { let utxos = await this.createUTXO(sends); for (let i = 0; i < utxos.length; i++) this.utxos.push(utxos[i]); return utxos; }; this.withdraw = (withdrawals) => Array.isArray(withdrawals) ? withdrawals.forEach(w => this.withdrawals.push(w)) : this.withdrawals.push(withdrawals); this.createTransactions = async (transactions) => { if (transactions === undefined) { const _nullifier_hashes = new Array(16).fill(this.zeroValue); for (let i = 0; i < this.utxoproofs.length; i++) { let _hash = await this.pedersen([this.utxoproofs[i].secret, this.utxoproofs[i].secret]); _nullifier_hashes[i] = _hash; } const _withdrawals = this.withdrawals; const _utxoProofs = this.utxoproofs; const _utxoOut = this.utxos; const _commitmentOut = new Array(16).fill(this.zeroValue); for (let u = 0; u < _utxoOut.length; u++) { _commitmentOut[u] = await this.pedersen([_utxoOut[u].owner, _utxoOut[u].amount, _utxoOut[u].assetType]); } let utxoTree = await this.makeUTXOTree(); transactions = [{ current_root: this.momiji.history.root(), deposit: this.depositTotal, tx_id: utxoTree.root(), withdrawals: new Array(16).fill(this.zeroHex).map(function (_, i, _w) { if (_withdrawals[i] !== undefined) return _withdrawals[i].amount; else return _w[i]; }), commitment_out: _commitmentOut, recipients: new Array(16).fill(this.zeroHex).map(function (_, i, _w) { if (_withdrawals[i] !== undefined) return _withdrawals[i].recipient; else return _w[i]; }), nullifier_hashes: _nullifier_hashes, utxo_in: new Array(16).fill(this.zeroHex).map(function (_, i, _ui) { if (_utxoProofs[i] !== undefined) return _utxoProofs[i]; else return { owner: "0x0000000000000000000000000000000000000000000000000000000000000000", amount: "0x0000000000000000000000000000000000000000000000000000000000000000", assetType: "0x0000000000000000000000000000000000000000000000000000000000000000" }; }), utxo_out: new Array(16).fill(this.zeroHex).map((_, i) => (_utxoOut[i] !== undefined) ? _utxoOut[i] : _utxoOut[i]) }]; } else { if (!Array.isArray(transactions)) transactions = [transactions]; } let provingQueue = Promise.resolve(); for (let i = 0; i < transactions.length; i++) { let thisTx = transactions[i]; let public_inputs = thisTx; let transaction = { pi_contract_hash: this.hashPublicInputs(public_inputs), current_root: thisTx.current_root, deposit: thisTx.deposit, withdrawals: thisTx.withdrawals, commitment_out: thisTx.commitment_out, recipients: thisTx.recipients, nullifier_hashes: thisTx.nullifier_hashes, tx_id: thisTx.tx_id, oracle: new Array(2 ** this.momiji.tree.utxoDepth).fill(this.zeroHex) .map((_, j, _oracle) => (thisTx.utxo_in[j].oracle !== undefined) ? thisTx.utxo_in[j].oracle : _oracle[j]), old_root_proof: new Array(2 ** this.momiji.tree.utxoDepth).fill(this.zeroHex) .map((_, j, _old_root_proof) => (thisTx.utxo_in[j].old_root_proof !== undefined) ? thisTx.utxo_in[j].old_root_proof : _old_root_proof[j]), secrets: new Array(2 ** this.momiji.tree.utxoDepth).fill(this.zeroHex) .map((_, j, _secrets) => thisTx.utxo_in[j].secret !== undefined ? thisTx.utxo_in[j].secret : _secrets[j]), utxo_in: new Array(2 ** this.momiji.tree.utxoDepth).fill(this.zeroHex) .map((_, j, _utxo_in) => [ thisTx.utxo_in[j].owner, thisTx.utxo_in[j].amount, thisTx.utxo_in[j].assetType ]) .flat(), utxo_out: (new Array(2 ** this.momiji.tree.utxoDepth).fill(this.zeroHex) .map((_, j) => thisTx.utxo_out[j] ? [thisTx.utxo_out[j].owner, thisTx.utxo_out[j].amount, thisTx.utxo_out[j].assetType] : new Array(3).fill(this.zeroHex))) .flat(), indexes: (new Array(2 ** this.momiji.tree.utxoDepth).fill(this.zeroHex) .map((_, j) => thisTx.utxo_in[j].indexes ? thisTx.utxo_in[j].indexes : new Array(3).fill(this.zeroHex))) .flat(), hash_path: (new Array(2 ** this.momiji.tree.utxoDepth).fill(this.zeroHex) .map((_, j) => thisTx.utxo_in[j].hash_path ? thisTx.utxo_in[j].hash_path : new Array(17).fill(this.zeroHex))) .flat(), }; provingQueue.then(async () => { let transactionProof = await this.proveTransaction(transaction, public_inputs); this.contractInputs.push(transactionProof); }); await provingQueue; } console.log(this.contractInputs); return this.contractInputs; }; } async spendUTXO(utxo) { let provenUTXOs = await this.makeUTXOProof(utxo); for (let i = 0; i < provenUTXOs.length; i++) this.utxoproofs.push(provenUTXOs[i]); return provenUTXOs; } async makeUTXOProof(utxo) { if (!Array.isArray(utxo)) utxo = [utxo]; let utxoProofList = []; for (let i = 0; i < utxo.length; i++) { const siblings = await this.fetchSiblings(utxo[i]); const commitment = await this.pedersen([utxo[i].owner, utxo[i].amount, utxo[i].assetType]); const oldRoot = await this.momiji.contract.getRootFromUtxo(commitment); const txBatch = await this.momiji.contract.getTxIds(oldRoot); let trees = { utxo_tree: await this.MerkleTree(this.momiji.tree.utxoDepth, siblings), tx_tree: await this.MerkleTree(this.momiji.tree.txDepth, txBatch), historic_tree: this.momiji.history, }; await Promise.all([ trees.utxo_tree.init(), trees.tx_tree.init(), trees.historic_tree.init() ]); let txId = trees.utxo_tree.root(); let txRoot = trees.tx_tree.root(); let oracle = await this.getOracle(oldRoot); let batch = await this.pedersen([txRoot, oracle]); let new_root = await this.pedersen([batch, oldRoot]); const utxoMerkleIndex = trees.utxo_tree.getIndex(commitment); const txMerkleIndex = trees.tx_tree.getIndex(txId); const historicMerkleIndex = trees.historic_tree.getIndex(new_root); const utxoMerkleProof = await trees.utxo_tree.proof(utxoMerkleIndex); const txMerkleProof = await trees.utxo_tree.proof(txMerkleIndex); const historicMerkleProof = await trees.utxo_tree.proof(historicMerkleIndex); const utxoProof = { secret: utxo[i].secret, owner: utxo[i].owner, amount: utxo[i].amount, assetType: utxo[i].assetType, nullifier_hash: await this.pedersen([utxo[i].secret, utxo[i].secret]), oracle: oracle, old_root_proof: oldRoot, current_root: trees.historic_tree.root(), indexes: [utxoMerkleIndex, txMerkleIndex, historicMerkleIndex].map(i => this.toFixedHex(i, true)), hash_path: [ ...utxoMerkleProof.pathElements, ...txMerkleProof.pathElements, ...historicMerkleProof.pathElements ] }; utxoProofList.push(utxoProof); } return utxoProofList; } async createUTXO(utxo) { if (!Array.isArray(utxo)) utxo = [utxo]; const utxoSet = []; await Promise.all(utxo.map(async (utxo) => { utxo.owner = (utxo.secret ? await this.pedersen(utxo.secret) : undefined); if (utxo.owner) utxoSet.push(utxo); })); return utxoSet; } viewBalance(assetType, token) { if (token) { } if (assetType) { } } } exports.TransactionBuilder = TransactionBuilder;