2021-06-08 20:50:34 +02:00
|
|
|
const { ethers } = require('hardhat')
|
|
|
|
const { BigNumber } = ethers
|
2021-06-16 02:07:32 +02:00
|
|
|
const { randomBN, poseidonHash, toBuffer } = require('./utils')
|
2021-06-15 14:48:26 +02:00
|
|
|
const Keypair = require('./keypair')
|
2021-06-08 20:50:34 +02:00
|
|
|
|
|
|
|
class Utxo {
|
2021-06-16 10:28:39 +02:00
|
|
|
/** Initialize a new UTXO - unspent transaction output or input. Note, a full TX consists of 2/16 inputs and 2 outputs
|
2021-06-16 02:07:32 +02:00
|
|
|
*
|
|
|
|
* @param {BigNumber | BigInt | number | string} amount UTXO amount
|
|
|
|
* @param {BigNumber | BigInt | number | string} blinding Blinding factor
|
|
|
|
* @param {Keypair} keypair
|
|
|
|
* @param {number|null} index UTXO index in the merkle tree
|
|
|
|
*/
|
2021-06-16 02:31:31 +02:00
|
|
|
constructor({ amount = 0, keypair = new Keypair(), blinding = randomBN(), index = null } = {}) {
|
2021-06-09 13:19:22 +02:00
|
|
|
this.amount = BigNumber.from(amount)
|
|
|
|
this.blinding = BigNumber.from(blinding)
|
|
|
|
this.keypair = keypair
|
2021-06-09 12:56:33 +02:00
|
|
|
this.index = index
|
2021-06-08 20:50:34 +02:00
|
|
|
}
|
|
|
|
|
2021-06-16 02:07:32 +02:00
|
|
|
/**
|
|
|
|
* Returns commitment for this UTXO
|
|
|
|
*
|
|
|
|
* @returns {BigNumber}
|
|
|
|
*/
|
2021-06-08 20:50:34 +02:00
|
|
|
getCommitment() {
|
|
|
|
if (!this._commitment) {
|
2021-06-09 13:19:22 +02:00
|
|
|
this._commitment = poseidonHash([this.amount, this.blinding, this.keypair.pubkey])
|
2021-06-08 20:50:34 +02:00
|
|
|
}
|
|
|
|
return this._commitment
|
|
|
|
}
|
|
|
|
|
2021-06-16 02:07:32 +02:00
|
|
|
/**
|
|
|
|
* Returns nullifier for this UTXO
|
|
|
|
*
|
|
|
|
* @returns {BigNumber}
|
|
|
|
*/
|
2021-06-08 20:50:34 +02:00
|
|
|
getNullifier() {
|
|
|
|
if (!this._nullifier) {
|
2021-06-16 02:31:31 +02:00
|
|
|
if (
|
|
|
|
this.amount > 0 &&
|
|
|
|
(this.index === undefined ||
|
|
|
|
this.index === null ||
|
|
|
|
this.keypair.privkey === undefined ||
|
|
|
|
this.keypair.privkey === null)
|
|
|
|
) {
|
2021-06-08 20:50:34 +02:00
|
|
|
throw new Error('Can not compute nullifier without utxo index or private key')
|
|
|
|
}
|
2021-06-09 13:19:22 +02:00
|
|
|
this._nullifier = poseidonHash([this.getCommitment(), this.index || 0, this.keypair.privkey || 0])
|
2021-06-08 20:50:34 +02:00
|
|
|
}
|
|
|
|
return this._nullifier
|
|
|
|
}
|
2021-06-16 01:50:06 +02:00
|
|
|
|
2021-06-16 02:07:32 +02:00
|
|
|
/**
|
|
|
|
* Encrypt UTXO data using the current keypair
|
|
|
|
*
|
|
|
|
* @returns {string} `0x`-prefixed hex string with data
|
|
|
|
*/
|
2021-06-16 01:50:06 +02:00
|
|
|
encrypt() {
|
2021-06-16 02:07:32 +02:00
|
|
|
const bytes = Buffer.concat([toBuffer(this.blinding, 31), toBuffer(this.amount, 31)])
|
2021-06-16 01:50:06 +02:00
|
|
|
return this.keypair.encrypt(bytes)
|
|
|
|
}
|
|
|
|
|
2021-06-16 02:07:32 +02:00
|
|
|
/**
|
|
|
|
* Decrypt a UTXO
|
|
|
|
*
|
|
|
|
* @param {Keypair} keypair keypair used to decrypt
|
|
|
|
* @param {string} data hex string with data
|
|
|
|
* @param {number} index UTXO index in merkle tree
|
|
|
|
* @returns {Utxo}
|
|
|
|
*/
|
2021-06-16 01:50:06 +02:00
|
|
|
static decrypt(keypair, data, index) {
|
|
|
|
const buf = keypair.decrypt(data)
|
|
|
|
return new Utxo({
|
|
|
|
blinding: BigNumber.from('0x' + buf.slice(0, 31).toString('hex')),
|
|
|
|
amount: BigNumber.from('0x' + buf.slice(31, 62).toString('hex')),
|
|
|
|
keypair,
|
|
|
|
index,
|
|
|
|
})
|
|
|
|
}
|
2021-06-08 20:50:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = Utxo
|