/
index.js
108 lines (82 loc) · 2.72 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
const bigIntBuffer = require('bigint-buffer')
const secp256k1 = require('noble-secp256k1')
const crypto = require('crypto')
function MultiSet() {
const curve = secp256k1.CURVE
function getBit(bytes, index) {
const byteIndex = index >>> 3
const b = bytes[byteIndex]
const bitMask = 0x01 << (7 - (0x07 & index))
return (b & bitMask) != 0x00
}
function convertToPoint(x, xBytes) {
if (x >= curve.p) return null // not on curve
const encodedCompressedPoint = Buffer.alloc(32 + 1)
const yCoordinateIsEven = !getBit(xBytes, 0)
const firstByte = yCoordinateIsEven ? COMPRESSED_FIRST_BYTE_0 : COMPRESSED_FIRST_BYTE_1
encodedCompressedPoint.writeInt32LE(firstByte)
xBytes.copy(encodedCompressedPoint, 1)
try {
return secp256k1.Point.fromHex(encodedCompressedPoint)
} catch (ex) {
// not on curve
return null
}
}
function getPoint(sha256Buffer) {
for (let n = BigInt(0); true; ++n) {
const hash = crypto.createHash('sha256')
let buf = Buffer.alloc(8)
buf.writeBigInt64LE(n)
hash.update(buf)
hash.update(sha256Buffer)
const xBytes = hash.digest()
//console.log("hash", n, xBytes.toString('hex'))
const point = convertToPoint(bigIntBuffer.toBigIntLE(xBytes), xBytes)
//console.log("ec point", n, point)
if (point !== null) return point
}
}
const COMPRESSED_FIRST_BYTE_0 = 0x02
const COMPRESSED_FIRST_BYTE_1 = 0x03
const EMPTY_HASH = Buffer.alloc(32)
let self = {
point: new secp256k1.Point(0, 0), // inf
addPoint(point) {
if (self.point.x === 0 && self.point.y === 0)
self.point = point
else if (point.x === 0 && point.y === 0)
; // noop
else
self.point = self.point.add(point)
},
addItem(sha256Buffer) {
if (sha256Buffer === null || sha256Buffer === EMPTY_HASH) return
self.addPoint(getPoint(sha256Buffer))
},
addSet(ms) {
self.addPoint(ms.point)
},
removeItem(sha256Buffer) {
if (sha256Buffer === null || sha256Buffer === EMPTY_HASH) return
const point = getPoint(sha256Buffer)
if (point.x === self.point.x && point.y === self.point.y)
self.point = new secp256k1.Point(0, 0)
else
self.point = self.point.subtract(point)
},
getHash() {
if (self.point.x === 0 && self.point.y === 0) {
return EMPTY_HASH.toString('hex')
}
const hash = crypto.createHash('sha256')
const bufX = bigIntBuffer.toBufferBE(self.point.x, 32)
hash.update(bufX)
const bufY = bigIntBuffer.toBufferBE(self.point.y, 32)
hash.update(bufY)
return hash.digest('hex')
}
}
return self
}
module.exports = MultiSet