const e = require("b4a"),
{
contract: {
hyperschema: { encode: t },
},
} = require("../../../../schema/spec"),
{
verifyAttestation: i,
verifyDeviceResponse: a,
decryptContactRequest: s,
generateMailboxKeyPair: r,
generateRoomCapabilities: o,
} = require("../../invitations"),
n = require("../registration"),
{ KeetContractError: c } = require("../errors"),
{ isMemberActive: d, getMemberForDeviceKey: y } = require("../query"),
{
profileRoomStats: l,
profileContactStats: u,
profileContactRequestStats: m,
} = require("../stats/profile"),
{ isValidBuildType: k } = require("../helpers"),
p = ["isActive", "isFullyMuted", "isMentionsOnlyMuted", "isFavorite"],
v = { IsActive: 1, IsFullyMuted: 2, IsMentionsOnlyMuted: 3, IsFavorite: 4 },
w = ["isBlocked"],
f = { IsBlocked: 1 },
b = ["isPending", "isAccepted", "isRejected", "isDiscarded", "isSeen"],
g = { IsPending: 1, IsAccepted: 2, IsRejected: 3, IsDiscarded: 4, IsSeen: 5 }
async function K({ sharedRooms: e, blockedContacts: t }, { db: i, writer: a }) {
const [s, r] = await Promise.all([y(i, a.key), i.get("@keet-private-id/counters")])
if (!d(s)) throw c.ABORT()
for (let t = 0; t < e.length; t++) {
const { roomKey: a, initiator: s, fastForwardTo: r, encryptionKey: n, seed: c } = e[t],
d = {
roomKey: a,
encryptionKey: n,
mailbox: o(a, c).mailboxKeyPair.publicKey,
clock: t + 1,
status: { isActive: !0, isFullyMuted: !1, isMentionsOnlyMuted: !1 },
initiator: s,
fastForwardTo: r,
identitySwap: null,
}
await i.insert("@keet-private-id/rooms", d)
}
for (let e = 0; e < t.length; e++) {
const a = r ? r.contacts + 1 : 0,
s = { memberKey: t[e], clock: a, status: { isBlocked: !0 }, mailbox: null, roomKey: null }
await i.insert("@keet-private-id/contacts", s)
}
}
async function A({ identity: t }, { db: i, writer: a }) {
const [s, r] = await Promise.all([y(i, a.key), i.get("@keet-private-id/identity")])
if (!d(s) || !r || !e.equals(t.publicKey, r.publicKey)) throw c.ABORT()
;(t.discoveryCoreKey && (r.discoveryCoreKey = t.discoveryCoreKey),
await i.insert("@keet-private-id/identity", r))
}
async function R({ key: e, settings: t }, { db: i, writer: a }) {
const [s, r] = await Promise.all([y(i, a.key), i.get("@keet-private-id/settings")])
if (!d(s)) throw c.ABORT()
const o = { ...r, [e]: t[e] }
await i.insert("@keet-private-id/settings", o)
}
async function h({ invitation: e }, { db: t, writer: i }) {
const [a, s] = await Promise.all([y(t, i.key), t.get("@keet/system-digest")])
if (!d(a)) throw c.ABORT()
const r = s.settings.buildType,
o = e.buildType
if (!k(r, o)) throw c.INVALID_BUILD_TYPE()
await t.insert("@keet-private-id/pending-invitations", e)
}
async function O({ id: e }, { db: t, writer: i }) {
const a = await y(t, i.key)
if (!d(a)) throw c.ABORT()
await t.delete("@keet-private-id/pending-invitations", { id: e })
}
async function T({ blindResponse: t }, { db: i, writer: s, hostcalls: o }) {
const n = await i.get("@keet/room-capabilities")
if (!n) throw c.ABORT()
let d = null
try {
const e = r(o.key, n.seed)
d = a(e, t)
} catch (e) {
throw c.ABORT()
}
if (!d) throw c.ABORT()
const { id: y, response: u } = d,
{ roomKeys: m } = u
if (!m.key || !m.encryptionKey) throw c.ABORT()
const [k, p, v, w] = await Promise.all([
i.get("@keet-private-id/pending-invitations", { id: y }),
i.get("@keet-private-id/rooms", { roomKey: m.key }),
i.get("@keet-private-id/counters"),
i.get("@keet/system-digest"),
])
if (!k) throw c.ABORT()
const f = v ? v.rooms + 1 : 0,
b = p
let g = p
if (
(g
? (!g.status.isActive || w.abi.version >= 12) &&
((g.clock = f), (g.status.isActive = !0), (g.status.isReadOnly = !1))
: (g = {
clock: f,
roomKey: m.key,
encryptionKey: m.encryptionKey,
fastForwardTo: m.fastForwardTo,
status: { isActive: !0, isReadOnly: !1 },
initiator: k.initiator,
mailbox: k.mailbox,
identitySwapV2: k.identity,
}),
await i.insert("@keet-private-id/rooms", g),
await l(i, g, b),
w.abi.version < 11)
)
await i.delete("@keet-private-id/pending-invitations", { id: y })
else
for await (const t of i.find("@keet-private-id/pending-invitations"))
e.equals(t.mailbox, g.mailbox) && (await i.delete("@keet-private-id/pending-invitations", t))
await o.ackWriter(s.key)
}
async function B({ header: e, ...t }, { db: i, writer: a }) {
const [s, r, o, n, u] = await Promise.all([
y(i, a.key),
i.get("@keet-private-id/rooms", { roomKey: t.roomKey }),
i.get("@keet-private-id/stats"),
i.get("@keet-private-id/counters"),
i.get("@keet/system-digest"),
])
if (!u || !d(s)) throw c.ABORT()
const m = r
let k = 0
u.abi.version < 10 ? (r ? (k = r.clock) : o && (k = o.rooms + 1)) : n && (k = n.rooms + 1)
let p = r?.status.isReadOnly
!p && t.isReadOnly && (p = !0)
const v = { ...t, clock: k, status: { isActive: !0, isReadOnly: p }, contact: t.dm }
;(await i.insert("@keet-private-id/rooms", v), await l(i, v, m))
}
async function q({ roomKey: e }, { db: t, writer: i }) {
const a = await y(t, i.key)
if (d(a)) return t.delete("@keet-private-id/rooms", { roomKey: e })
}
async function P({ header: t, roomKey: i, key: a, status: s }, { db: r, writer: o }) {
const [n, u, m, k] = await Promise.all([
y(r, o.key),
r.get("@keet-private-id/rooms", { roomKey: i }),
r.get("@keet-private-id/counters"),
r.get("@keet/system-digest"),
])
if (!d(n) || !u) throw c.ABORT()
if (!L(a)) throw c.ABORT()
const p = u,
v = E(a),
w = m.rooms + 1
if (
((u.status = { ...u.status, [v]: s[v] }),
(u.clock = w),
!u.status.isActive && k.abi.version >= 12)
)
for await (const t of r.find("@keet-private-id/pending-invitations"))
e.equals(t.mailbox, u.mailbox) && (await r.delete("@keet-private-id/pending-invitations", t))
;(await r.insert("@keet-private-id/rooms", u), await l(r, u, p))
}
async function x(
{ contact: { memberKey: e, mailbox: t, roomKey: i, status: a } },
{ db: s, writer: r },
) {
const [o, n, l] = await Promise.all([
y(s, r.key),
s.get("@keet-private-id/contacts", { memberKey: e }),
s.get("@keet-private-id/counters"),
])
if (!d(o)) throw c.ABORT()
const m = n,
k = l ? l.contacts + 1 : 0
let p = n
;(p
? ((p.mailbox = t), (p.status = a), (p.clock = k))
: (p = { memberKey: e, mailbox: t, status: a, clock: k }),
await s.insert("@keet-private-id/contacts", p),
await u(s, p, m))
}
async function I({ memberKey: e, key: t, status: i }, { db: a, writer: s }) {
const [r, o, n] = await Promise.all([
y(a, s.key),
a.get("@keet-private-id/contacts", { memberKey: e }),
a.get("@keet-private-id/counters"),
])
if (!d(r) || !o) throw c.ABORT()
if (!_(t)) throw c.ABORT()
const l = o,
m = W(t)
;((o.status = { ...o.status, [m]: i[m] }),
(o.clock = n ? n.contacts + 1 : 0),
await a.insert("@keet-private-id/contacts", o),
await u(a, o, l))
}
async function S({ memberKey: e }, { db: t, writer: i }) {
const [a, s] = await Promise.all([
y(t, i.key),
t.get("@keet-private-id/contacts", { memberKey: e }),
])
if (!d(a) || !s) throw c.ABORT
await t.delete("@keet-private-id/contacts", s)
}
async function M({ blindRequest: e, header: a }, { db: o, hostcalls: n, writer: d }) {
const y = await o.get("@keet/room-capabilities")
if (!y) throw c.ABORT()
let l = null
try {
const t = r(n.key, y.seed)
l = s(t, e)
} catch (e) {
throw c.ABORT()
}
if (!l) throw c.ABORT()
const { body: u, attestation: k } = l,
{ sender: p, keys: v, mailbox: w, message: f } = u,
[b, g, K] = await Promise.all([
o.get("@keet-private-id/contacts", { memberKey: p.attestation.memberKey }),
o.get("@keet-private-id/stats"),
o.get("@keet/system-digest"),
])
if (b && b.status.isBlocked) throw c.ABORT()
if (b && b.roomKey && K.abi.version < 12) throw c.ABORT()
const A = t("@keet-private-id/contact-request-body", u),
R = t("@keet/member-profile", p.profile)
if (!i(k, A)) throw c.ABORT()
if (!i(p.attestation, R)) throw c.ABORT()
let h = !0
const O = a.timestamp,
T = o.find("@keet-private-id/pending-contact-requests-by-member", {
gte: { memberKey: p.attestation.memberKey },
lte: { memberKey: p.attestation.memberKey },
})
for await (const e of T)
e.sentAt > O
? (h = !1)
: ((e.status.isPending = !1),
(e.status.isDiscarded = !0),
await o.insert("@keet-private-id/contact-requests-v2", e))
if (!h) return
const B = g?.contactRequests || 0,
q = {
memberKey: p.attestation.memberKey,
sender: p.profile,
status: { isPending: !0 },
clock: B,
keys: v,
mailbox: w,
message: f,
sentAt: O,
}
;(await o.insert("@keet-private-id/contact-requests-v2", q),
await m(o, q, null),
await n.ackWriter(d.key))
}
async function C({ memberKey: e, roomKey: t, key: i, status: a }, { db: s, writer: r }) {
if (!t) throw c.ABORT()
const [o, n, m] = await Promise.all([
y(s, r.key),
s.get("@keet-private-id/contact-requests-v2", { memberKey: e, keys: { key: t } }),
s.get("@keet-private-id/counters"),
])
if (!d(o) || !n) throw c.ABORT()
if (!U(i)) throw c.ABORT()
const k = N(i),
p = n.sender,
v = !n.status.isAccepted && i === g.IsAccepted,
w = i === g.IsSeen
if (!w && n.status.isAccepted) throw c.ABORT()
const f = {
isPending: !1,
isAccepted: !1,
isRejected: !1,
isDiscarded: !1,
isSeen: n.status.isSeen,
},
b = w ? n.status : f
if (
((n.status = { ...b, [k]: a[k] }),
(n.status.isAccepted || n.status.isRejected || n.status.isDiscarded) &&
(n.status.isPending = !1),
await s.insert("@keet-private-id/contact-requests-v2", n),
!v)
)
return
const K = (m?.rooms || 0) + 1,
A = (m?.contacts || 0) + 1,
R = {
clock: K,
roomKey: n.keys.key,
encryptionKey: n.keys.encryptionKey,
mailbox: n.mailbox,
status: { isActive: !0 },
},
h = { memberKey: n.memberKey, mailbox: p.mailbox, clock: A, status: { isBlocked: !1 } }
;(await s.insert("@keet-private-id/rooms", R),
await l(s, R, null),
await s.insert("@keet-private-id/contacts", h),
await u(s, h, null))
}
async function F({ memberKey: e }, { db: t, writer: i }) {}
async function D({ key: e }, { db: t, writer: i }) {
const a = await y(t, i.key)
if (!d(a)) throw c.ABORT()
await t.insert("@keet-private-id/unreads-v2", { deviceKey: i.key, key: e })
}
async function j({ key: e }, { db: t, writer: i }) {
const a = await y(t, i.key)
if (!d(a)) throw c.ABORT()
await t.delete("@keet-private-id/unreads-v2", { deviceKey: i.key, key: e })
}
function V(e) {
;(e.add("@keet-private-id/run-v3-migration", K),
e.add("@keet-private-id/update-identity", A),
e.add("@keet-private-id/update-settings", R),
e.add("@keet-private-id/add-room", B),
e.add("@keet-private-id/remove-room", q),
e.add("@keet-private-id/update-room-status", P),
e.add("@keet-private-id/add-pending-invitation", h),
e.add("@keet-private-id/remove-pending-invitation", O),
e.add("@keet-private-id/resolve-pending-invitation", T),
e.add("@keet-private-id/add-contact", x),
e.add("@keet-private-id/update-contact-status", I),
e.add("@keet-private-id/remove-contact", S),
e.add("@keet-private-id/add-contact-request", M),
e.add("@keet-private-id/update-contact-request-status", C),
e.add("@keet-private-id/activate-contact-room", F),
e.add("@keet-private-id/add-unreads-key", D),
e.add("@keet-private-id/remove-unreads-key", j))
}
function E(e) {
return p[e - 1]
}
function L(e) {
return e >= 1 && e <= p.length
}
function W(e) {
return w[e - 1]
}
function _(e) {
return e >= 1 && e <= w.length
}
function N(e) {
return b[e - 1]
}
function U(e) {
return e >= 1 && e <= b.length
}
module.exports = { [n]: V, RoomStatus: v, ContactStatus: f, ContactRequestStatus: g }