mirror of
https://github.com/zkopru-network/zkopru.git
synced 2026-02-19 10:04:41 -05:00
fix: note encryption & decription
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
"dependencies": {
|
||||
"@zkopru/babyjubjub": "file:../babyjubjub",
|
||||
"@zkopru/prisma": "file:../prisma",
|
||||
"@zkopru/transaction": "file:../transaction",
|
||||
"@zkopru/utils": "file:../utils",
|
||||
"bip39": "^3.0.2",
|
||||
"hdkey": "^1.1.1",
|
||||
|
||||
@@ -3,13 +3,14 @@ import Web3 from 'web3'
|
||||
import { Account, EncryptedKeystoreV3Json, AddAccount } from 'web3-core'
|
||||
import { Field, Point, EdDSA, signEdDSA, verifyEdDSA } from '@zkopru/babyjubjub'
|
||||
import { Keystore } from '@zkopru/prisma'
|
||||
import { Note, ZkTx } from '@zkopru/transaction'
|
||||
import { hexify } from '@zkopru/utils'
|
||||
import assert from 'assert'
|
||||
|
||||
export class ZkAccount {
|
||||
private snarkPK: Field
|
||||
|
||||
ethPK: string
|
||||
private ethPK: string
|
||||
|
||||
address: string
|
||||
|
||||
@@ -58,6 +59,27 @@ export class ZkAccount {
|
||||
}
|
||||
}
|
||||
|
||||
decrypt(zkTx: ZkTx): Note | undefined {
|
||||
const { memo } = zkTx
|
||||
if (!memo) {
|
||||
return
|
||||
}
|
||||
let note: Note | undefined
|
||||
for (const outflow of zkTx.outflow) {
|
||||
try {
|
||||
note = Note.decrypt({
|
||||
utxoHash: outflow.note,
|
||||
memo,
|
||||
privKey: this.snarkPK.toHex(32),
|
||||
})
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
if (note) break
|
||||
}
|
||||
return note
|
||||
}
|
||||
|
||||
static fromEncryptedKeystoreV3Json(
|
||||
obj: EncryptedKeystoreV3Json,
|
||||
password: string,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"references": [
|
||||
{ "path": "../babyjubjub/tsconfig.build.json", "prepend": false },
|
||||
{ "path": "../prisma/tsconfig.build.json", "prepend": false },
|
||||
{ "path": "../transaction/tsconfig.build.json", "prepend": false },
|
||||
{ "path": "../utils/tsconfig.build.json", "prepend": false }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"blake-hash": "^1.1.0",
|
||||
"bn.js": "^5.1.1",
|
||||
"circomlib": "^0.1.1",
|
||||
"snarkjs": "^0.1.25",
|
||||
"ffjavascript": "^0.1.2",
|
||||
"soltypes": "^1.2.0",
|
||||
"web3-utils": "^1.2.6"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { hexToBuffer, hexify } from '@zkopru/utils'
|
||||
import bigInt, { BigInteger } from 'big-integer'
|
||||
import * as snarkjs from 'snarkjs'
|
||||
import * as ffjs from 'ffjavascript'
|
||||
import * as circomlib from 'circomlib'
|
||||
import createBlakeHash from 'blake-hash'
|
||||
import { Field, F } from './field'
|
||||
@@ -49,19 +49,20 @@ export class Point {
|
||||
return Point.from(result[0].toString(), result[1].toString())
|
||||
}
|
||||
|
||||
static getMultiplier(key: string): Field {
|
||||
const sBuff = circomlib.eddsa.pruneBuffer(
|
||||
static getMultiplier(key: string | Buffer): Field {
|
||||
const buff: Buffer = typeof key === 'string' ? hexToBuffer(key) : key
|
||||
const sBuff = Buffer.from(
|
||||
createBlakeHash('blake512')
|
||||
.update(key)
|
||||
.update(buff)
|
||||
.digest()
|
||||
.slice(0, 32),
|
||||
)
|
||||
return Field.from(
|
||||
snarkjs.bigInt
|
||||
.leBuff2int(sBuff)
|
||||
.shr(3)
|
||||
.toString(),
|
||||
)
|
||||
sBuff[0] &= 0xf8
|
||||
sBuff[31] &= 0x7f
|
||||
sBuff[31] |= 0x40
|
||||
const s = ffjs.utils.leBuff2int(sBuff)
|
||||
const multiplier = ffjs.Scalar.shr(s, 3)
|
||||
return Field.from(multiplier)
|
||||
}
|
||||
|
||||
static isOnJubjub(x: F, y: F): boolean {
|
||||
|
||||
@@ -65,5 +65,11 @@ describe('baby jubjub point', () => {
|
||||
})
|
||||
expect(verifyEdDSA(msg, signature, pubKey)).toBe(true)
|
||||
})
|
||||
describe('fromPrivKey', () => {
|
||||
it('should be able to same public key using Point.getMultiplier()', () => {
|
||||
const multiplier = Point.getMultiplier(password)
|
||||
expect(Point.BASE8.mul(multiplier).toHex()).toBe(pubKey.toHex())
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -216,18 +216,19 @@ export class Note {
|
||||
utxoHash: Field
|
||||
memo: Buffer
|
||||
privKey: string
|
||||
}): Note | null {
|
||||
}): Note | undefined {
|
||||
const multiplier = Point.getMultiplier(privKey)
|
||||
const ephemeralPubKey = Point.decode(memo.subarray(0, 32))
|
||||
const sharedKey = ephemeralPubKey.mul(multiplier).encode()
|
||||
const data = memo.subarray(32, 81)
|
||||
const decrypted = chacha20.decrypt(sharedKey, 0, data) // prints "testing"
|
||||
|
||||
const decrypted = chacha20.decrypt(sharedKey, 0, data)
|
||||
const salt = Field.fromBuffer(decrypted.subarray(0, 16))
|
||||
const tokenAddress = TokenUtils.getTokenAddress(
|
||||
decrypted.subarray(16, 17)[0],
|
||||
)
|
||||
if (tokenAddress === null) return null
|
||||
if (tokenAddress === null) {
|
||||
return
|
||||
}
|
||||
const value = Field.fromBuffer(decrypted.subarray(17, 49))
|
||||
|
||||
const myPubKey: Point = Point.fromPrivKey(privKey)
|
||||
@@ -262,6 +263,6 @@ export class Note {
|
||||
return nftNote
|
||||
}
|
||||
}
|
||||
return null
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import { BigInteger } from 'big-integer'
|
||||
import { soliditySha3 } from 'web3-utils'
|
||||
import * as snarkjs from 'snarkjs'
|
||||
import { Field } from '@zkopru/babyjubjub'
|
||||
import * as Utils from '@zkopru/utils'
|
||||
import { Bytes32 } from 'soltypes'
|
||||
@@ -297,19 +296,21 @@ export class ZkTx {
|
||||
}
|
||||
|
||||
circomProof(): {
|
||||
pi_a: bigint[]
|
||||
pi_b: bigint[][]
|
||||
pi_c: bigint[]
|
||||
pi_a: BigInteger[]
|
||||
pi_b: BigInteger[][]
|
||||
pi_c: BigInteger[]
|
||||
protocol: string
|
||||
} {
|
||||
if (!this.proof) throw Error('Does not have SNARK proof')
|
||||
const bigOne = Field.from(1).toIden3BigInt()
|
||||
const bigZero = Field.zero.toIden3BigInt()
|
||||
return {
|
||||
pi_a: [...this.proof.pi_a.map(f => f.toIden3BigInt()), snarkjs.bigInt(1)],
|
||||
pi_a: [...this.proof.pi_a.map(f => f.toIden3BigInt()), bigOne],
|
||||
pi_b: [
|
||||
...this.proof.pi_b.map(arr => arr.map(f => f.toIden3BigInt())),
|
||||
[snarkjs.bigInt(1), snarkjs.bigInt(0)],
|
||||
[bigOne, bigZero],
|
||||
],
|
||||
pi_c: [...this.proof.pi_c.map(f => f.toIden3BigInt()), snarkjs.bigInt(1)],
|
||||
pi_c: [...this.proof.pi_c.map(f => f.toIden3BigInt()), bigOne],
|
||||
protocol: 'groth',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ export class ZkWallet {
|
||||
const utxoSqls = await this.db.prisma.note.findMany({
|
||||
where: {
|
||||
pubKey: { in: [account.pubKey.toHex()] },
|
||||
usedFor: null,
|
||||
},
|
||||
})
|
||||
const utxos: Utxo[] = []
|
||||
@@ -338,6 +339,7 @@ export class ZkWallet {
|
||||
const { verifier } = this.node
|
||||
const snarkValid = await verifier.snarkVerifier.verifyTx(zkTx)
|
||||
assert(snarkValid, 'generated snark proof is invalid')
|
||||
assert(zkTx.memo, 'memo does not exist')
|
||||
const response = await fetch(`${this.coordinator}/tx`, {
|
||||
method: 'post',
|
||||
body: zkTx.encode().toString('hex'),
|
||||
|
||||
@@ -54,6 +54,7 @@ export class ZkWizard {
|
||||
account: ZkAccount
|
||||
toMemo?: number
|
||||
}): Promise<ZkTx> {
|
||||
logger.info(`shield to memo(${toMemo})`)
|
||||
return new Promise<ZkTx>((resolve, reject) => {
|
||||
const merkleProof: { [hash: string]: MerkleProof<Field> } = {}
|
||||
const eddsa: { [hash: string]: EdDSA } = {}
|
||||
@@ -240,6 +241,10 @@ export class ZkWizard {
|
||||
)
|
||||
// let { proof, publicSignals } = Utils.genProof(snarkjs.unstringifyBigInts(provingKey), witness);
|
||||
// TODO handle genProof exception
|
||||
let memo: Buffer | undefined
|
||||
if (toMemo !== undefined) {
|
||||
memo = tx.outflow[toMemo].encrypt()
|
||||
}
|
||||
const zkTx: ZkTx = new ZkTx({
|
||||
inflow: tx.inflow.map((utxo, index) => {
|
||||
return {
|
||||
@@ -257,7 +262,7 @@ export class ZkWizard {
|
||||
pi_c: proof.pi_c.map(Field.from),
|
||||
},
|
||||
swap: tx.swap,
|
||||
memo: toMemo ? tx.outflow[toMemo].encrypt() : undefined,
|
||||
memo,
|
||||
})
|
||||
return zkTx
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user