const nowUtcToBigint = (epoch) => BigInt(Math.floor((Date.now() - epoch) / 60000)); const encodeItem = (aliveCardinality, alive, dpi) => { return BigInt(alive + aliveCardinality * dpi) }; const encodeShare = async (clientAsn, items) => { // encoder always takes latest file const h = await import('./helpers.js'); const commit = await h.getLastCommitBigint(); const ts = nowUtcToBigint(h.EPOCH_MS); const asn = BigInt(clientAsn); const sortedItems = Object.keys(items).sort().map(k => items[k]); const itemsTotalBits = h.ENDPOINT_STATE_BITS * sortedItems.length; const bufSize = Math.ceil((h.COMMIT_BITS + h.TIMESTAMP_BITS + h.ASN_BITS + itemsTotalBits) / 8); const buf = new Uint8Array(bufSize); h.writeBits(buf, 0, h.COMMIT_BITS, commit); h.writeBits(buf, h.COMMIT_BITS, h.TIMESTAMP_BITS, ts); h.writeBits(buf, h.COMMIT_BITS + h.TIMESTAMP_BITS, h.ASN_BITS, asn); let itemOffset = h.COMMIT_BITS + h.TIMESTAMP_BITS + h.ASN_BITS; for (const x of sortedItems) { const encodedItem = encodeItem(h.ALIVE_CARDINALITY, x.alive, x.dpi); h.writeBits(buf, itemOffset, h.ENDPOINT_STATE_BITS, encodedItem); itemOffset += h.ENDPOINT_STATE_BITS; } // we should not xor commit bytes for further identification h.setXor(buf, h.XOR_KEY, h.COMMIT_BYTES); const base64 = buf.toBase64({ alphabet: "base64url", omitPadding: true }); console.log("encoded share:", base64); return base64; }