Skip to content

Commit ba04b20

Browse files
committed
test: add Backup tests + utils
1 parent 01563be commit ba04b20

4 files changed

Lines changed: 129 additions & 1 deletion

File tree

test/ETHPrivateBank.test.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import { ethers } from 'hardhat'
33
import {
44
generateDeposit,
55
generateProof,
6+
packBackupData,
67
randomBigInt,
78
toSolidityInput,
9+
unpackBackupData,
810
verifyProof,
911
} from '../utils/PrivateBank.utils'
1012
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
@@ -21,6 +23,7 @@ import {
2123
} from '../utils/types.utils'
2224
import { poseidon } from '@iden3/js-crypto'
2325
import { FIELD_P } from '../utils/keccak.utils'
26+
import { decrypt, encrypt, generateEncryptionKey } from '../utils/aes256.utils'
2427

2528
describe('ETHPrivateBank', function () {
2629
const ZERO = '0x29319238daf40223d6021718c846ac2a0c0ef028ecc765972e999a8ac79662a8'
@@ -291,6 +294,77 @@ describe('ETHPrivateBank', function () {
291294
expect(isSpent).to.be.true
292295
})
293296

297+
it('backup(): should backup note and emit event, be able to withdraw using the note', async () => {
298+
// generate deposit
299+
const deposit = generateDeposit()
300+
301+
// encrypt backup data
302+
const plainText = packBackupData(deposit.secret, deposit.nullifier)
303+
const encryptionKey = generateEncryptionKey()
304+
const encryptedData = encrypt(encryptionKey, plainText)
305+
306+
// setup tree
307+
const hashMiMCSponge = await getMiMCSpongeHasher()
308+
const tree = new FixedMerkleTree(HEIGHT, [], {
309+
hashFunction: hashMiMCSponge,
310+
zeroElement: ZERO,
311+
})
312+
tree.insert(deposit.commitment)
313+
314+
// deposit to contract
315+
await expect(privateBank.connect(sender).deposit(deposit.commitment, { value: DEPOSIT_AMOUNT }))
316+
.to.not.be.reverted
317+
318+
// backup note
319+
const tx = await privateBank.connect(sender).backup(encryptedData)
320+
const receipt = await tx.wait()
321+
const backupEvt = receipt.events[0]
322+
expect(backupEvt.event).to.equal('Backup')
323+
expect(backupEvt.args.user).to.equal(sender.address)
324+
expect(backupEvt.args.encryptedData).to.equal(encryptedData)
325+
326+
// calculate relayer fee
327+
const gasPrice = await ethers.provider.getGasPrice()
328+
const gasFee = relayerFeeGasUsed.mul(gasPrice)
329+
const relayerFee = DEPOSIT_AMOUNT.mul(relayerFeePct)
330+
.div(relayerFeePctBase)
331+
.add(gasFee)
332+
.toBigInt()
333+
334+
// retrieve backed up data
335+
const decrypted = decrypt(backupEvt.args.encryptedData, encryptionKey)
336+
const { secret: retrievedSecret, nullifier: retrievedNullifier } = unpackBackupData(decrypted)
337+
338+
// generate proof
339+
const rawInput = {
340+
root: tree.root,
341+
nullifierHash: poseidon.hash([retrievedNullifier]),
342+
nullifier: retrievedNullifier,
343+
relayer: relayer.address,
344+
recipient: recipient.address,
345+
relayerFee,
346+
gasRefund,
347+
secret: retrievedSecret,
348+
pathElements: tree.path(0).pathElements,
349+
pathIndices: tree.path(0).pathIndices,
350+
}
351+
const input = utils.stringifyBigInts(rawInput)
352+
const proofData = await generateProof(input)
353+
const proof = toSolidityInput(proofData)
354+
355+
// withdraw
356+
const inputs = {
357+
root: padHex(rawInput.root as string),
358+
nullifierHash: bigintToHex(rawInput.nullifierHash),
359+
nullifier: bigintToHex(rawInput.nullifier),
360+
recipient: recipient.address,
361+
relayer: relayer.address,
362+
relayerFee: bigintToHex(rawInput.relayerFee),
363+
gasRefund: bigintToHex(rawInput.gasRefund),
364+
}
365+
await expect(privateBank.connect(relayer).withdraw(proof.proof, inputs)).to.not.be.reverted
366+
})
367+
294368
it('withdraw(): should prevent double spend', async () => {
295369
// generate deposit
296370
const deposit = generateDeposit()

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"strict": true,
88
"skipLibCheck": true,
99
"resolveJsonModule": true,
10-
"types": ["chai", "mocha"],
10+
"types": ["node", "chai", "mocha"],
1111
"typeRoots": ["./types", "./node_modules/@types"]
1212
}
1313
}

utils/PrivateBank.utils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ export const generateDeposit = (seed?: string) => {
1616
return deposit
1717
}
1818

19+
export const packBackupData = (secret: Buffer, nullifier: Buffer) => {
20+
return secret.toString('hex').padStart(64, '0') + nullifier.toString('hex').padStart(64, '0')
21+
}
22+
23+
export const unpackBackupData = (backupData: string) => {
24+
const secret = BigInt('0x' + backupData.slice(0, 64))
25+
const nullifier = BigInt('0x' + backupData.slice(64, 128))
26+
return { secret, nullifier }
27+
}
28+
1929
export const randomBigInt = (numBytes: number, seed?: string) => {
2030
let bytes: Buffer
2131
if (seed) {

utils/aes256.utils.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import crypto from 'crypto'
2+
3+
const KEY_BITS = 256
4+
const IV_LEN = 16
5+
6+
export const generateEncryptionKey = () => {
7+
return crypto.randomBytes(KEY_BITS / 8)
8+
}
9+
10+
export const encrypt = (encryptionKey: Buffer, plainText: string) => {
11+
const iv = crypto.randomBytes(IV_LEN)
12+
const cipher = crypto.createCipheriv(`aes-${KEY_BITS}-cbc`, encryptionKey, iv)
13+
14+
let cipherText = cipher.update(plainText, 'utf8', 'hex')
15+
cipherText += cipher.final('hex')
16+
17+
const ivString = iv.toString('hex')
18+
const encryptedData = `0x${ivString}${cipherText}`
19+
return encryptedData
20+
}
21+
22+
export const decrypt = (encryptedData: string, encryptionKey: Buffer) => {
23+
const withoutPrefix = encryptedData.startsWith('0x') ? encryptedData.slice(2) : encryptedData
24+
const ivString = withoutPrefix.slice(0, 32)
25+
const cipherText = withoutPrefix.slice(32)
26+
const iv = Buffer.from(ivString, 'hex')
27+
const decipher = crypto.createDecipheriv(`aes-${KEY_BITS}-cbc`, encryptionKey, iv)
28+
let decrypted = decipher.update(cipherText, 'hex', 'utf8')
29+
decrypted += decipher.final('utf8')
30+
return decrypted
31+
}
32+
33+
// EXAMPLE
34+
35+
// const example = () => {
36+
// const encryptionKey = generateEncryptionKey()
37+
// const plainText =
38+
// '657cee92c25a700343dbfef8501d5d2952f5f20206f827dac22af39f974ca7b085f978041c8be47cd250821d1512bd9c60576da0f6d42a012c5f75180b02'
39+
// const encryptedData = encrypt(encryptionKey, plainText)
40+
// const decrypted = decrypt(encryptedData, encryptionKey)
41+
// console.log({ decrypted })
42+
// }
43+
44+
// example()

0 commit comments

Comments
 (0)