Merge branch 'unstable' into userconfig_disappearingmessage

pull/2861/head
William Grant 2 years ago
commit 905fe5aab9

@ -102,10 +102,10 @@
"glob": "7.1.2", "glob": "7.1.2",
"image-type": "^4.1.0", "image-type": "^4.1.0",
"ip2country": "1.0.1", "ip2country": "1.0.1",
"libsession_util_nodejs": "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.2.0/libsession_util_nodejs-v0.2.0.tar.gz", "libsession_util_nodejs": "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.2.2/libsession_util_nodejs-v0.2.2.tar.gz",
"libsodium-wrappers-sumo": "^0.7.9", "libsodium-wrappers-sumo": "^0.7.9",
"lodash": "^4.17.21",
"linkify-it": "^4.0.1", "linkify-it": "^4.0.1",
"lodash": "^4.17.21",
"long": "^4.0.0", "long": "^4.0.0",
"mic-recorder-to-mp3": "^2.2.2", "mic-recorder-to-mp3": "^2.2.2",
"moment": "^2.29.4", "moment": "^2.29.4",
@ -161,8 +161,8 @@
"@types/firstline": "^2.0.2", "@types/firstline": "^2.0.2",
"@types/fs-extra": "5.0.5", "@types/fs-extra": "5.0.5",
"@types/libsodium-wrappers-sumo": "^0.7.5", "@types/libsodium-wrappers-sumo": "^0.7.5",
"@types/lodash": "^4.14.194",
"@types/linkify-it": "^3.0.2", "@types/linkify-it": "^3.0.2",
"@types/lodash": "^4.14.194",
"@types/mocha": "5.0.0", "@types/mocha": "5.0.0",
"@types/mustache": "^4.1.2", "@types/mustache": "^4.1.2",
"@types/node-fetch": "^2.5.7", "@types/node-fetch": "^2.5.7",

@ -54,6 +54,7 @@ type Props = SessionMessageListProps & {
const StyledMessagesContainer = styled.div<{}>` const StyledMessagesContainer = styled.div<{}>`
display: flex; display: flex;
flex-grow: 1; flex-grow: 1;
gap: var(--margins-xxs);
flex-direction: column-reverse; flex-direction: column-reverse;
position: relative; position: relative;
overflow-x: hidden; overflow-x: hidden;

@ -6,7 +6,7 @@ import { MessageRenderingProps } from '../../../../models/messageType';
import { findCachedBlindedMatchOrLookItUp } from '../../../../session/apis/open_group_api/sogsv3/knownBlindedkeys'; import { findCachedBlindedMatchOrLookItUp } from '../../../../session/apis/open_group_api/sogsv3/knownBlindedkeys';
import { getConversationController } from '../../../../session/conversations'; import { getConversationController } from '../../../../session/conversations';
import { getSodiumRenderer } from '../../../../session/crypto'; import { getSodiumRenderer } from '../../../../session/crypto';
import { PubKey } from '../../../../session/types'; import { KeyPrefixType, PubKey } from '../../../../session/types';
import { openConversationWithMessages } from '../../../../state/ducks/conversations'; import { openConversationWithMessages } from '../../../../state/ducks/conversations';
import { updateUserDetailsModal } from '../../../../state/ducks/modalDialog'; import { updateUserDetailsModal } from '../../../../state/ducks/modalDialog';
import { import {
@ -61,7 +61,7 @@ export const MessageAvatar = (props: Props) => {
const userName = authorName || authorProfileName || sender; const userName = authorName || authorProfileName || sender;
const onMessageAvatarClick = useCallback(async () => { const onMessageAvatarClick = useCallback(async () => {
if (isPublic && !PubKey.hasBlindedPrefix(sender)) { if (isPublic && !PubKey.isBlinded(sender)) {
// public chat but session id not blinded. disable showing user details if we do not have an active convo with that user. // public chat but session id not blinded. disable showing user details if we do not have an active convo with that user.
// an unactive convo with that user means that we never chatted with that id directyly, but only through a sogs // an unactive convo with that user means that we never chatted with that id directyly, but only through a sogs
const convoWithSender = getConversationController().get(sender); const convoWithSender = getConversationController().get(sender);
@ -84,6 +84,11 @@ export const MessageAvatar = (props: Props) => {
} }
if (isPublic && selectedConvoKey) { if (isPublic && selectedConvoKey) {
if (sender.startsWith(KeyPrefixType.blinded25)) {
window.log.info('onMessageAvatarClick: blinded25 convo click are disabled currently...');
return;
}
const convoOpen = getConversationController().get(selectedConvoKey); const convoOpen = getConversationController().get(selectedConvoKey);
const room = OpenGroupData.getV2OpenGroupRoom(convoOpen.id); const room = OpenGroupData.getV2OpenGroupRoom(convoOpen.id);
let privateConvoToOpen = sender; let privateConvoToOpen = sender;

@ -46,6 +46,8 @@ const StyledMessageContentContainer = styled.div<{ direction: 'left' | 'right' }
const StyledMessageWithAuthor = styled.div<{ isIncoming: boolean }>` const StyledMessageWithAuthor = styled.div<{ isIncoming: boolean }>`
max-width: ${props => (props.isIncoming ? '100%' : 'calc(100% - 17px)')}; max-width: ${props => (props.isIncoming ? '100%' : 'calc(100% - 17px)')};
display: flex;
flex-direction: column;
`; `;
export const MessageContentWithStatuses = (props: Props) => { export const MessageContentWithStatuses = (props: Props) => {

@ -1,17 +1,17 @@
import classNames from 'classnames'; import classNames from 'classnames';
import React from 'react'; import React from 'react';
import { isImageAttachment } from '../../../../types/Attachment'; import { useDispatch } from 'react-redux';
import { Image } from '../../Image';
import { MessageRenderingProps } from '../../../../models/messageType'; import { MessageRenderingProps } from '../../../../models/messageType';
import { useDispatch, useSelector } from 'react-redux';
import { getIsMessageSelectionMode } from '../../../../state/selectors/conversations';
import { SessionIcon } from '../../../icon';
import { showLinkVisitWarningDialog } from '../../../dialog/SessionConfirm';
import { import {
useMessageAttachments, useMessageAttachments,
useMessageDirection, useMessageDirection,
useMessageLinkPreview, useMessageLinkPreview,
} from '../../../../state/selectors'; } from '../../../../state/selectors';
import { useIsMessageSelectionMode } from '../../../../state/selectors/selectedConversation';
import { isImageAttachment } from '../../../../types/Attachment';
import { showLinkVisitWarningDialog } from '../../../dialog/SessionConfirm';
import { SessionIcon } from '../../../icon';
import { Image } from '../../Image';
export type MessageLinkPreviewSelectorProps = Pick< export type MessageLinkPreviewSelectorProps = Pick<
MessageRenderingProps, MessageRenderingProps,
@ -30,7 +30,7 @@ export const MessageLinkPreview = (props: Props) => {
const direction = useMessageDirection(props.messageId); const direction = useMessageDirection(props.messageId);
const attachments = useMessageAttachments(props.messageId); const attachments = useMessageAttachments(props.messageId);
const previews = useMessageLinkPreview(props.messageId); const previews = useMessageLinkPreview(props.messageId);
const isMessageSelectionMode = useSelector(getIsMessageSelectionMode); const isMessageSelectionMode = useIsMessageSelectionMode();
if (!props.messageId) { if (!props.messageId) {
return null; return null;

@ -64,8 +64,13 @@ export const MessageQuote = (props: Props) => {
// If the quote is not found in memory, we try to find it in the DB // If the quote is not found in memory, we try to find it in the DB
if (quoteNotFound && quote.id && quote.author) { if (quoteNotFound && quote.id && quote.author) {
// We always look for the quote by sentAt timestamp, for opengroups, closed groups and session chats
// this will return an array of sent messages by id that we have locally.
const quotedMessagesCollection = await Data.getMessagesBySenderAndSentAt([ const quotedMessagesCollection = await Data.getMessagesBySenderAndSentAt([
{ timestamp: toNumber(quote.id), source: quote.author }, {
timestamp: toNumber(quote.id),
source: quote.author,
},
]); ]);
if (quotedMessagesCollection?.length) { if (quotedMessagesCollection?.length) {

@ -2,11 +2,12 @@ import React, { MouseEvent, useState } from 'react';
import * as MIME from '../../../../../types/MIME'; import * as MIME from '../../../../../types/MIME';
import { isEmpty } from 'lodash';
import styled from 'styled-components';
import { useIsMessageSelectionMode } from '../../../../../state/selectors/selectedConversation';
import { QuoteAuthor } from './QuoteAuthor'; import { QuoteAuthor } from './QuoteAuthor';
import { QuoteText } from './QuoteText';
import { QuoteIconContainer } from './QuoteIconContainer'; import { QuoteIconContainer } from './QuoteIconContainer';
import styled from 'styled-components'; import { QuoteText } from './QuoteText';
import { isEmpty } from 'lodash';
const StyledQuoteContainer = styled.div` const StyledQuoteContainer = styled.div`
min-width: 300px; // if the quoted content is small it doesn't look very good so we set a minimum min-width: 300px; // if the quoted content is small it doesn't look very good so we set a minimum
@ -69,6 +70,7 @@ export interface QuotedAttachmentType {
} }
export const Quote = (props: QuoteProps) => { export const Quote = (props: QuoteProps) => {
const isSelectionMode = useIsMessageSelectionMode();
const { isIncoming, attachment, text, referencedMessageNotFound, onClick } = props; const { isIncoming, attachment, text, referencedMessageNotFound, onClick } = props;
const [imageBroken, setImageBroken] = useState(false); const [imageBroken, setImageBroken] = useState(false);
@ -81,7 +83,11 @@ export const Quote = (props: QuoteProps) => {
<StyledQuote <StyledQuote
hasAttachment={Boolean(!isEmpty(attachment))} hasAttachment={Boolean(!isEmpty(attachment))}
isIncoming={isIncoming} isIncoming={isIncoming}
onClick={onClick} onClick={e => {
if (onClick && !isSelectionMode) {
onClick(e);
}
}}
> >
<QuoteIconContainer <QuoteIconContainer
attachment={attachment} attachment={attachment}

@ -50,8 +50,7 @@ const StyledReadableMessage = styled(ReadableMessage)<{
align-items: center; align-items: center;
width: 100%; width: 100%;
letter-spacing: 0.03rem; letter-spacing: 0.03rem;
padding: var(--margins-xs) var(--margins-lg) 0; padding: 0 var(--margins-lg) 0;
margin: var(--margins-xxs) 0;
&.message-highlighted { &.message-highlighted {
animation: ${highlightedMessageAnimation} 1s ease-in-out; animation: ${highlightedMessageAnimation} 1s ease-in-out;

@ -1,13 +1,14 @@
import React, { ReactElement, useRef, useState } from 'react'; import React, { ReactElement, useRef, useState } from 'react';
import { SortedReactionList } from '../../../../types/Reaction'; import { useMouse } from 'react-use';
import styled from 'styled-components';
import { isUsAnySogsFromCache } from '../../../../session/apis/open_group_api/sogsv3/knownBlindedkeys';
import { UserUtils } from '../../../../session/utils'; import { UserUtils } from '../../../../session/utils';
import { useIsMessageSelectionMode } from '../../../../state/selectors/selectedConversation';
import { SortedReactionList } from '../../../../types/Reaction';
import { abbreviateNumber } from '../../../../util/abbreviateNumber'; import { abbreviateNumber } from '../../../../util/abbreviateNumber';
import { nativeEmojiData } from '../../../../util/emoji'; import { nativeEmojiData } from '../../../../util/emoji';
import styled from 'styled-components';
import { useMouse } from 'react-use';
import { POPUP_WIDTH, ReactionPopup, TipPosition } from './ReactionPopup';
import { popupXDefault, popupYDefault } from '../message-content/MessageReactions'; import { popupXDefault, popupYDefault } from '../message-content/MessageReactions';
import { isUsAnySogsFromCache } from '../../../../session/apis/open_group_api/sogsv3/knownBlindedkeys'; import { POPUP_WIDTH, ReactionPopup, TipPosition } from './ReactionPopup';
const StyledReaction = styled.button<{ selected: boolean; inModal: boolean; showCount: boolean }>` const StyledReaction = styled.button<{ selected: boolean; inModal: boolean; showCount: boolean }>`
display: flex; display: flex;
@ -68,6 +69,8 @@ export const Reaction = (props: ReactionProps): ReactElement => {
handlePopupReaction, handlePopupReaction,
handlePopupClick, handlePopupClick,
} = props; } = props;
const isMessageSelection = useIsMessageSelectionMode();
const reactionsMap = (reactions && Object.fromEntries(reactions)) || {}; const reactionsMap = (reactions && Object.fromEntries(reactions)) || {};
const senders = reactionsMap[emoji]?.senders || []; const senders = reactionsMap[emoji]?.senders || [];
const count = reactionsMap[emoji]?.count; const count = reactionsMap[emoji]?.count;
@ -93,7 +96,9 @@ export const Reaction = (props: ReactionProps): ReactElement => {
}; };
const handleReactionClick = () => { const handleReactionClick = () => {
if (!isMessageSelection) {
onClick(emoji); onClick(emoji);
}
}; };
return ( return (
@ -104,7 +109,7 @@ export const Reaction = (props: ReactionProps): ReactElement => {
inModal={inModal} inModal={inModal}
onClick={handleReactionClick} onClick={handleReactionClick}
onMouseEnter={() => { onMouseEnter={() => {
if (inGroup) { if (inGroup && !isMessageSelection) {
const { innerWidth: windowWidth } = window; const { innerWidth: windowWidth } = window;
if (handlePopupReaction) { if (handlePopupReaction) {
// overflow on far right means we shift left // overflow on far right means we shift left

@ -357,7 +357,7 @@ export const CopyMenuItem = (): JSX.Element | null => {
export const MarkAllReadMenuItem = (): JSX.Element | null => { export const MarkAllReadMenuItem = (): JSX.Element | null => {
const convoId = useConvoIdFromContext(); const convoId = useConvoIdFromContext();
const isIncomingRequest = useIsIncomingRequest(convoId); const isIncomingRequest = useIsIncomingRequest(convoId);
if (!isIncomingRequest && !PubKey.hasBlindedPrefix(convoId)) { if (!isIncomingRequest && !PubKey.isBlinded(convoId)) {
return ( return (
<Item onClick={() => markAllReadByConvoId(convoId)}>{window.i18n('markAllAsRead')}</Item> <Item onClick={() => markAllReadByConvoId(convoId)}>{window.i18n('markAllAsRead')}</Item>
); );
@ -379,7 +379,7 @@ export const BlockMenuItem = (): JSX.Element | null => {
const isPrivate = useIsPrivate(convoId); const isPrivate = useIsPrivate(convoId);
const isIncomingRequest = useIsIncomingRequest(convoId); const isIncomingRequest = useIsIncomingRequest(convoId);
if (!isMe && isPrivate && !isIncomingRequest && !PubKey.hasBlindedPrefix(convoId)) { if (!isMe && isPrivate && !isIncomingRequest && !PubKey.isBlinded(convoId)) {
const blockTitle = isBlocked ? window.i18n('unblock') : window.i18n('block'); const blockTitle = isBlocked ? window.i18n('unblock') : window.i18n('block');
const blockHandler = isBlocked const blockHandler = isBlocked
? () => unblockConvoById(convoId) ? () => unblockConvoById(convoId)

@ -104,7 +104,7 @@ export function useIsBlinded(convoId?: string) {
if (!convoId) { if (!convoId) {
return false; return false;
} }
return Boolean(PubKey.hasBlindedPrefix(convoId)); return Boolean(PubKey.isBlinded(convoId));
} }
export function useHasNickname(convoId?: string) { export function useHasNickname(convoId?: string) {

@ -546,8 +546,8 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
); );
const hasIncomingMessages = incomingMessageCount > 0; const hasIncomingMessages = incomingMessageCount > 0;
if (this.id.startsWith('15')) { if (PubKey.isBlinded(this.id)) {
window.log.info('Sending a blinded message to this user: ', this.id); window.log.info('Sending a blinded message react to this user: ', this.id);
await this.sendBlindedMessageRequest(chatMessageParams); await this.sendBlindedMessageRequest(chatMessageParams);
return; return;
} }
@ -1759,7 +1759,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
); );
const hasIncomingMessages = incomingMessageCount > 0; const hasIncomingMessages = incomingMessageCount > 0;
if (this.id.startsWith('15')) { if (PubKey.isBlinded(this.id)) {
window.log.info('Sending a blinded message to this user: ', this.id); window.log.info('Sending a blinded message to this user: ', this.id);
await this.sendBlindedMessageRequest(chatMessageParams); await this.sendBlindedMessageRequest(chatMessageParams);
return; return;
@ -1872,7 +1872,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
const ourSignKeyBytes = await UserUtils.getUserED25519KeyPairBytes(); const ourSignKeyBytes = await UserUtils.getUserED25519KeyPairBytes();
const groupUrl = this.getSogsOriginMessage(); const groupUrl = this.getSogsOriginMessage();
if (!PubKey.hasBlindedPrefix(this.id)) { if (!PubKey.isBlinded(this.id)) {
window?.log?.warn('sendBlindedMessageRequest - convo is not a blinded one'); window?.log?.warn('sendBlindedMessageRequest - convo is not a blinded one');
return; return;
} }
@ -1920,7 +1920,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
} }
this.set({ active_at: Date.now(), isApproved: true }); this.set({ active_at: Date.now(), isApproved: true });
// TODO we need to add support for sending blinded25 message request in addition to the legacy blinded15
await getMessageQueue().sendToOpenGroupV2BlindedRequest({ await getMessageQueue().sendToOpenGroupV2BlindedRequest({
encryptedContent: encryptedMsg, encryptedContent: encryptedMsg,
roomInfos: roomInfo, roomInfos: roomInfo,

@ -1376,7 +1376,7 @@ export function findAndFormatContact(pubkey: string): FindAndFormatContactType {
if ( if (
pubkey === UserUtils.getOurPubKeyStrFromCache() || pubkey === UserUtils.getOurPubKeyStrFromCache() ||
(pubkey && pubkey.startsWith('15') && isUsAnySogsFromCache(pubkey)) (pubkey && PubKey.isBlinded(pubkey) && isUsAnySogsFromCache(pubkey))
) { ) {
profileName = window.i18n('you'); profileName = window.i18n('you');
isMe = true; isMe = true;

@ -1677,7 +1677,7 @@ function updateToSessionSchemaVersion31(currentVersion: number, db: BetterSqlite
// this filter is based on the `isContactToStoreInWrapper` function. Note, blocked contacts won't be added to the wrapper at first, but will on the first start // this filter is based on the `isContactToStoreInWrapper` function. Note, blocked contacts won't be added to the wrapper at first, but will on the first start
const contactsToWriteInWrapper = db const contactsToWriteInWrapper = db
.prepare( .prepare(
`SELECT * FROM ${CONVERSATIONS_TABLE} WHERE type = 'private' AND active_at > 0 AND priority <> ${CONVERSATION_PRIORITIES.hidden} AND (didApproveMe OR isApproved) AND id <> '$us' AND id NOT LIKE '15%' ;` `SELECT * FROM ${CONVERSATIONS_TABLE} WHERE type = 'private' AND active_at > 0 AND priority <> ${CONVERSATION_PRIORITIES.hidden} AND (didApproveMe OR isApproved) AND id <> '$us' AND id NOT LIKE '15%' AND id NOT LIKE '25%' ;`
) )
.all({ .all({
us: publicKeyHex, us: publicKeyHex,

@ -18,6 +18,7 @@ import { GoogleChrome } from '../util';
import { setExpirationStartTimestamp } from '../util/expiringMessages'; import { setExpirationStartTimestamp } from '../util/expiringMessages';
import { LinkPreviews } from '../util/linkPreviews'; import { LinkPreviews } from '../util/linkPreviews';
import { ReleasedFeatures } from '../util/releaseFeature'; import { ReleasedFeatures } from '../util/releaseFeature';
import { PropsForMessageWithoutConvoProps, lookupQuote } from '../state/ducks/conversations';
function contentTypeSupported(type: string): boolean { function contentTypeSupported(type: string): boolean {
const Chrome = GoogleChrome; const Chrome = GoogleChrome;
@ -50,26 +51,48 @@ async function copyFromQuotedMessage(
const id = _.toNumber(quoteId); const id = _.toNumber(quoteId);
// First we try to look for the quote in memory
const stateConversations = window.inboxStore?.getState().conversations;
const { messages, quotes } = stateConversations;
let quotedMessage: PropsForMessageWithoutConvoProps | MessageModel | undefined = lookupQuote(
quotes,
messages,
id,
quote.author
)?.propsForMessage;
// If the quote is not found in memory, we try to find it in the DB
if (!quotedMessage) {
// We always look for the quote by sentAt timestamp, for opengroups, closed groups and session chats // We always look for the quote by sentAt timestamp, for opengroups, closed groups and session chats
// this will return an array of sent message by id we have locally. // this will return an array of sent messages by id that we have locally.
const quotedMessagesCollection = await Data.getMessagesBySenderAndSentAt([
const collection = await Data.getMessagesBySentAt(id); {
// we now must make sure this is the sender we expect timestamp: id,
const found = collection.find(message => { source: quote.author,
return Boolean(author === message.get('source')); },
}); ]);
if (quotedMessagesCollection?.length) {
quotedMessage = quotedMessagesCollection.at(0);
}
}
if (!found) { if (!quotedMessage) {
window?.log?.warn(`We did not found quoted message ${id} with author ${author}.`); window?.log?.warn(`We did not found quoted message ${id} with author ${author}.`);
quoteLocal.referencedMessageNotFound = true; quoteLocal.referencedMessageNotFound = true;
msg.set({ quote: quoteLocal }); msg.set({ quote: quoteLocal });
return; return;
} }
const isMessageModelType = Boolean((quotedMessage as MessageModel).get !== undefined);
window?.log?.info(`Found quoted message id: ${id}`); window?.log?.info(`Found quoted message id: ${id}`);
quoteLocal.referencedMessageNotFound = false; quoteLocal.referencedMessageNotFound = false;
// NOTE we send the entire body to be consistent with the other platforms // NOTE we send the entire body to be consistent with the other platforms
quoteLocal.text = found.get('body') || ''; quoteLocal.text =
(isMessageModelType
? (quotedMessage as MessageModel).get('body')
: (quotedMessage as PropsForMessageWithoutConvoProps).text) || '';
// no attachments, just save the quote with the body // no attachments, just save the quote with the body
if ( if (
@ -83,7 +106,10 @@ async function copyFromQuotedMessage(
firstAttachment.thumbnail = null; firstAttachment.thumbnail = null;
const queryAttachments = found.get('attachments') || []; const queryAttachments =
(isMessageModelType
? (quotedMessage as MessageModel).get('attachments')
: (quotedMessage as PropsForMessageWithoutConvoProps).attachments) || [];
if (queryAttachments.length > 0) { if (queryAttachments.length > 0) {
const queryFirst = queryAttachments[0]; const queryFirst = queryAttachments[0];
@ -97,7 +123,10 @@ async function copyFromQuotedMessage(
} }
} }
const queryPreview = found.get('preview') || []; const queryPreview =
(isMessageModelType
? (quotedMessage as MessageModel).get('preview')
: (quotedMessage as PropsForMessageWithoutConvoProps).previews) || [];
if (queryPreview.length > 0) { if (queryPreview.length > 0) {
const queryFirst = queryPreview[0]; const queryFirst = queryPreview[0];
const { image } = queryFirst; const { image } = queryFirst;

@ -145,8 +145,8 @@ export function tryMatchBlindWithStandardKey(
throw new Error('standardKey must be a standard key (starting with 05)'); throw new Error('standardKey must be a standard key (starting with 05)');
} }
if (!blindedSessionId.startsWith(KeyPrefixType.blinded)) { if (!PubKey.isBlinded(blindedSessionId)) {
throw new Error('blindedKey must be a blinded key (starting with 15)'); throw new Error('blindedKey must be a blinded key (starting with 15 or 25)');
} }
// We don't want to stop iterating even if an error happens while looking for a blind/standard match. // We don't want to stop iterating even if an error happens while looking for a blind/standard match.
@ -204,7 +204,7 @@ function findNotCachedBlindingMatch(
// we iterate only over the convos private, approved, and which have an unblinded id. // we iterate only over the convos private, approved, and which have an unblinded id.
const foundConvoMatchingBlindedPubkey = getConversationController() const foundConvoMatchingBlindedPubkey = getConversationController()
.getConversations() .getConversations()
.filter(m => m.isPrivate() && m.isApproved() && !PubKey.hasBlindedPrefix(m.id)) .filter(m => m.isPrivate() && m.isApproved() && !PubKey.isBlinded(m.id))
.find(m => { .find(m => {
return tryMatchBlindWithStandardKey(m.id, blindedId, serverPublicKey, sodium); return tryMatchBlindWithStandardKey(m.id, blindedId, serverPublicKey, sodium);
}); });
@ -220,7 +220,7 @@ function findNotCachedBlindingMatch(
export function isUsAnySogsFromCache(blindedOrNakedId: string): boolean { export function isUsAnySogsFromCache(blindedOrNakedId: string): boolean {
const usUnblinded = UserUtils.getOurPubKeyStrFromCache(); const usUnblinded = UserUtils.getOurPubKeyStrFromCache();
if (!PubKey.hasBlindedPrefix(blindedOrNakedId)) { if (!PubKey.isBlinded(blindedOrNakedId)) {
return blindedOrNakedId === usUnblinded; return blindedOrNakedId === usUnblinded;
} }
const found = assertLoaded().find( const found = assertLoaded().find(
@ -268,7 +268,7 @@ function findNotCachedBlindedConvoFromUnblindedKey(
serverPublicKey: string, serverPublicKey: string,
sodium: LibSodiumWrappers sodium: LibSodiumWrappers
): Array<ConversationModel> { ): Array<ConversationModel> {
if (PubKey.hasBlindedPrefix(unblindedID)) { if (PubKey.isBlinded(unblindedID)) {
throw new Error( throw new Error(
'findNotCachedBlindedConvoFromUnblindedKey unblindedID is supposed to be unblinded!' 'findNotCachedBlindedConvoFromUnblindedKey unblindedID is supposed to be unblinded!'
); );
@ -279,7 +279,7 @@ function findNotCachedBlindedConvoFromUnblindedKey(
const foundConvosForThisServerPk = const foundConvosForThisServerPk =
getConversationController() getConversationController()
.getConversations() .getConversations()
.filter(m => m.isPrivate() && PubKey.hasBlindedPrefix(m.id) && m.isActive()) .filter(m => m.isPrivate() && PubKey.isBlinded(m.id) && m.isActive())
.filter(m => { .filter(m => {
return tryMatchBlindWithStandardKey(unblindedID, m.id, serverPublicKey, sodium); return tryMatchBlindWithStandardKey(unblindedID, m.id, serverPublicKey, sodium);
}) || []; }) || [];
@ -302,7 +302,7 @@ export async function findCachedBlindedMatchOrLookItUp(
serverPubKey: string, serverPubKey: string,
sodium: LibSodiumWrappers sodium: LibSodiumWrappers
): Promise<string | undefined> { ): Promise<string | undefined> {
if (!PubKey.hasBlindedPrefix(blindedId)) { if (!PubKey.isBlinded(blindedId)) {
return blindedId; return blindedId;
} }
const found = getCachedNakedKeyFromBlinded(blindedId, serverPubKey); const found = getCachedNakedKeyFromBlinded(blindedId, serverPubKey);
@ -333,7 +333,7 @@ export function findCachedBlindedIdFromUnblinded(
unblindedId: string, unblindedId: string,
serverPubKey: string serverPubKey: string
): string | undefined { ): string | undefined {
if (PubKey.hasBlindedPrefix(unblindedId)) { if (PubKey.isBlinded(unblindedId)) {
throw new Error('findCachedBlindedIdFromUnblinded needs an unblindedID'); throw new Error('findCachedBlindedIdFromUnblinded needs an unblindedID');
} }
const found = assertLoaded().find( const found = assertLoaded().find(
@ -351,7 +351,7 @@ export async function findCachedOurBlindedPubkeyOrLookItUp(
): Promise<string> { ): Promise<string> {
const ourNakedSessionID = UserUtils.getOurPubKeyStrFromCache(); const ourNakedSessionID = UserUtils.getOurPubKeyStrFromCache();
if (PubKey.hasBlindedPrefix(ourNakedSessionID)) { if (PubKey.isBlinded(ourNakedSessionID)) {
throw new Error('findCachedBlindedIdFromUnblindedOrLookItUp needs a unblindedID'); throw new Error('findCachedBlindedIdFromUnblindedOrLookItUp needs a unblindedID');
} }
let found = findCachedBlindedIdFromUnblinded(ourNakedSessionID, serverPubKey); let found = findCachedBlindedIdFromUnblinded(ourNakedSessionID, serverPubKey);
@ -402,7 +402,7 @@ export function findCachedBlindedMatchOrLookupOnAllServers(
unblindedId: string, unblindedId: string,
sodium: LibSodiumWrappers sodium: LibSodiumWrappers
): Array<ConversationModel> { ): Array<ConversationModel> {
if (PubKey.hasBlindedPrefix(unblindedId)) { if (PubKey.isBlinded(unblindedId)) {
throw new Error('findCachedBlindedMatchOrLookupOnAllServers needs an unblindedId'); throw new Error('findCachedBlindedMatchOrLookupOnAllServers needs an unblindedId');
} }

@ -63,7 +63,8 @@ async function getOpenGroupHeaders(data: {
const blindingValues = getBlindingValues(serverPK, signingKeys, sodium); const blindingValues = getBlindingValues(serverPK, signingKeys, sodium);
ka = blindingValues.secretKey; ka = blindingValues.secretKey;
kA = blindingValues.publicKey; kA = blindingValues.publicKey;
pubkey = `${KeyPrefixType.blinded}${toHex(kA)}`; // TODO we will need to add the support of blinded25 here
pubkey = `${KeyPrefixType.blinded15}${toHex(kA)}`;
} else { } else {
pubkey = `${KeyPrefixType.unblinded}${toHex(signingKeys.pubKeyBytes)}`; pubkey = `${KeyPrefixType.unblinded}${toHex(signingKeys.pubKeyBytes)}`;
} }
@ -151,7 +152,8 @@ const getBlindedPubKey = (
sodium: LibSodiumWrappers sodium: LibSodiumWrappers
): string => { ): string => {
const blindedPubKeyBytes = getBlindingValues(serverPK, signingKeys, sodium); const blindedPubKeyBytes = getBlindingValues(serverPK, signingKeys, sodium);
return `${KeyPrefixType.blinded}${to_hex(blindedPubKeyBytes.publicKey)}`; // TODO we will need to add the support of blinded25 here
return `${KeyPrefixType.blinded15}${to_hex(blindedPubKeyBytes.publicKey)}`;
}; };
const getBlindingValues = ( const getBlindingValues = (

@ -71,9 +71,6 @@ export const FEATURE_RELEASE_TIMESTAMPS = {
// NOTE for testing purposes only // NOTE for testing purposes only
// DISAPPEARING_MESSAGES_V2: 1677488400000, // unix 27/02/2023 09:00 // DISAPPEARING_MESSAGES_V2: 1677488400000, // unix 27/02/2023 09:00
// TODO update to agreed value between platforms for `user_config_libsession` USER_CONFIG: 1690761600000, // Monday July 31st at 10am Melbourne time
// FIXME once we are done with testing the user config over libsession feature // USER_CONFIG: 1677488400000, // testing: unix 27/02/2023 09:00
// FIXME the flag is forced on currently in releaseFeature.ts
USER_CONFIG: 1677488400000, // testing: unix 27/02/2023 09:00
// return 1706778000000; // unix 01/02/2024 09:00;
}; };

@ -187,7 +187,7 @@ export class ConversationController {
'getConversationController().deleteBlindedContact() needs complete initial fetch' 'getConversationController().deleteBlindedContact() needs complete initial fetch'
); );
} }
if (!PubKey.hasBlindedPrefix(blindedId)) { if (!PubKey.isBlinded(blindedId)) {
throw new Error('deleteBlindedContact allow accepts blinded id'); throw new Error('deleteBlindedContact allow accepts blinded id');
} }
window.log.info(`deleteBlindedContact with ${blindedId}`); window.log.info(`deleteBlindedContact with ${blindedId}`);

@ -139,7 +139,8 @@ export class MessageQueue {
recipientBlindedId: string; recipientBlindedId: string;
}) { }) {
try { try {
if (!PubKey.hasBlindedPrefix(recipientBlindedId)) { // TODO we will need to add the support for blinded25 messages requests
if (!PubKey.isBlinded(recipientBlindedId)) {
throw new Error('sendToOpenGroupV2BlindedRequest needs a blindedId'); throw new Error('sendToOpenGroupV2BlindedRequest needs a blindedId');
} }
const { serverTimestamp, serverId } = await MessageSender.sendToOpenGroupV2BlindedRequest( const { serverTimestamp, serverId } = await MessageSender.sendToOpenGroupV2BlindedRequest(

@ -10,9 +10,15 @@ export enum KeyPrefixType {
*/ */
standard = '05', standard = '05',
/** /**
* used for participants in open groups * used for participants in open groups (legacy blinding logic)
*/ */
blinded = '15', blinded15 = '15',
/**
* used for participants in open groups (new blinding logic)
*/
blinded25 = '25',
/** /**
* used for participants in open groups * used for participants in open groups
*/ */
@ -32,7 +38,7 @@ export class PubKey {
public static readonly PREFIX_GROUP_TEXTSECURE = '__textsecure_group__!'; public static readonly PREFIX_GROUP_TEXTSECURE = '__textsecure_group__!';
// prettier-ignore // prettier-ignore
private static readonly regex: RegExp = new RegExp( private static readonly regex: RegExp = new RegExp(
`^(${PubKey.PREFIX_GROUP_TEXTSECURE})?(${KeyPrefixType.standard}|${KeyPrefixType.blinded}|${KeyPrefixType.unblinded}|${KeyPrefixType.groupV3})?(${PubKey.HEX}{64}|${PubKey.HEX}{32})$` `^(${PubKey.PREFIX_GROUP_TEXTSECURE})?(${KeyPrefixType.standard}|${KeyPrefixType.blinded15}|${KeyPrefixType.blinded25}|${KeyPrefixType.unblinded}|${KeyPrefixType.groupV3})?(${PubKey.HEX}{64}|${PubKey.HEX}{32})$`
); );
/** /**
* If you want to update this regex. Be sure that those are matches ; * If you want to update this regex. Be sure that those are matches ;
@ -146,18 +152,19 @@ export class PubKey {
/** /**
* @param keyWithOrWithoutPrefix Key with or without prefix * @param keyWithOrWithoutPrefix Key with or without prefix
* @returns If key is the correct length and has a supported prefix 05 or 15 * @returns If key is the correct length and has a supported prefix 05, 15, 25
*/ */
public static isValidPrefixAndLength(keyWithOrWithoutPrefix: string): boolean { public static isValidPrefixAndLength(keyWithOrWithoutPrefix: string): boolean {
return ( return (
keyWithOrWithoutPrefix.length === 66 && keyWithOrWithoutPrefix.length === 66 &&
(keyWithOrWithoutPrefix.startsWith(KeyPrefixType.blinded) || (keyWithOrWithoutPrefix.startsWith(KeyPrefixType.blinded15) ||
keyWithOrWithoutPrefix.startsWith(KeyPrefixType.blinded25) ||
keyWithOrWithoutPrefix.startsWith(KeyPrefixType.standard)) keyWithOrWithoutPrefix.startsWith(KeyPrefixType.standard))
); );
} }
/** /**
* This removes the 05 or 15 prefix from a Pubkey which have it and have a length of 66 * This removes the 05, 15 or 25 prefix from a Pubkey which have it and have a length of 66
* @param keyWithOrWithoutPrefix the key with or without the prefix * @param keyWithOrWithoutPrefix the key with or without the prefix
*/ */
public static removePrefixIfNeeded(keyWithOrWithoutPrefix: string): string { public static removePrefixIfNeeded(keyWithOrWithoutPrefix: string): string {
@ -230,8 +237,8 @@ export class PubKey {
return fromHexToArray(PubKey.removePrefixIfNeeded(this.key)); return fromHexToArray(PubKey.removePrefixIfNeeded(this.key));
} }
public static hasBlindedPrefix(key: string) { public static isBlinded(key: string) {
return key.startsWith(KeyPrefixType.blinded); return key.startsWith(KeyPrefixType.blinded15) || key.startsWith(KeyPrefixType.blinded25);
} }
public static isClosedGroupV3(key: string) { public static isClosedGroupV3(key: string) {

@ -29,9 +29,7 @@ const mappedContactWrapperValues = new Map<string, ContactInfo>();
* We want to sync the message request status so we need to allow a contact even if it's not approved, did not approve us and is not blocked. * We want to sync the message request status so we need to allow a contact even if it's not approved, did not approve us and is not blocked.
*/ */
function isContactToStoreInWrapper(convo: ConversationModel): boolean { function isContactToStoreInWrapper(convo: ConversationModel): boolean {
return ( return !convo.isMe() && convo.isPrivate() && convo.isActive() && !PubKey.isBlinded(convo.id);
!convo.isMe() && convo.isPrivate() && convo.isActive() && !PubKey.hasBlindedPrefix(convo.id)
);
} }
/** /**

@ -206,7 +206,7 @@ const getValidContacts = (convos: Array<ConversationModel>) => {
c.getRealSessionUsername() && c.getRealSessionUsername() &&
c.isPrivate() && c.isPrivate() &&
c.isApproved() && c.isApproved() &&
!PubKey.hasBlindedPrefix(c.get('id')) !PubKey.isBlinded(c.get('id'))
); );
const contacts = contactsModels.map(c => { const contacts = contactsModels.map(c => {

@ -1174,6 +1174,7 @@ export async function openConversationToSpecificMessage(args: {
/** /**
* Look for quote matching the timestamp and author in the quote lookup map * Look for quote matching the timestamp and author in the quote lookup map
* @param quotes - the lookup map of the selected conversations quotes * @param quotes - the lookup map of the selected conversations quotes
* @param messages - the messages in memory for the selected conversation
* @param author - the pubkey of the quoted author * @param author - the pubkey of the quoted author
* @param timestamp - usually the id prop on the quote object of a message * @param timestamp - usually the id prop on the quote object of a message
* @returns - the message model if found, undefined otherwise * @returns - the message model if found, undefined otherwise

@ -800,7 +800,7 @@ export const getMessageQuoteProps = createSelector(
const isFromMe = isUsAnySogsFromCache(author) || false; const isFromMe = isUsAnySogsFromCache(author) || false;
// NOTE the quote lookup map always stores our messages using the unblinded pubkey // NOTE the quote lookup map always stores our messages using the unblinded pubkey
if (isFromMe && PubKey.hasBlindedPrefix(author)) { if (isFromMe && PubKey.isBlinded(author)) {
author = UserUtils.getOurPubKeyStrFromCache(); author = UserUtils.getOurPubKeyStrFromCache();
} }

@ -418,7 +418,7 @@ describe('knownBlindedKeys', () => {
it('throws if blindedSessionId is not standard', () => { it('throws if blindedSessionId is not standard', () => {
expect(() => { expect(() => {
tryMatchBlindWithStandardKey(realSessionId, realSessionId, serverPublicKey, sodium); tryMatchBlindWithStandardKey(realSessionId, realSessionId, serverPublicKey, sodium);
}).to.throw('blindedKey must be a blinded key (starting with 15)'); }).to.throw('blindedKey must be a blinded key (starting with 15 or 25)');
}); });
it('returns true if those keys are not matching blind & naked', () => { it('returns true if those keys are not matching blind & naked', () => {

@ -207,7 +207,7 @@ describe('SwarmPolling', () => {
await swarmPolling.start(true); await swarmPolling.start(true);
expect(pollOnceForKeySpy.callCount).to.eq(1); expect(pollOnceForKeySpy.callCount).to.eq(1);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0]]);
}); });
it('does run for our pubkey even if activeAt is recent ', async () => { it('does run for our pubkey even if activeAt is recent ', async () => {
@ -219,7 +219,7 @@ describe('SwarmPolling', () => {
await swarmPolling.start(true); await swarmPolling.start(true);
expect(pollOnceForKeySpy.callCount).to.eq(1); expect(pollOnceForKeySpy.callCount).to.eq(1);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0]]);
}); });
describe('legacy group', () => { describe('legacy group', () => {
@ -236,7 +236,7 @@ describe('SwarmPolling', () => {
// our pubkey will be polled for, hence the 2 // our pubkey will be polled for, hence the 2
expect(pollOnceForKeySpy.callCount).to.eq(2); expect(pollOnceForKeySpy.callCount).to.eq(2);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]); expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]);
}); });
@ -262,7 +262,7 @@ describe('SwarmPolling', () => {
// our pubkey will be polled for, hence the 2 // our pubkey will be polled for, hence the 2
expect(pollOnceForKeySpy.callCount).to.eq(2); expect(pollOnceForKeySpy.callCount).to.eq(2);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]); expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]);
getItemByIdStub.restore(); getItemByIdStub.restore();
getItemByIdStub = TestUtils.stubData('getItemById'); getItemByIdStub = TestUtils.stubData('getItemById');
@ -288,9 +288,9 @@ describe('SwarmPolling', () => {
await swarmPolling.pollForAllKeys(); await swarmPolling.pollForAllKeys();
expect(pollOnceForKeySpy.callCount).to.eq(3); expect(pollOnceForKeySpy.callCount).to.eq(3);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]); expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]);
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, [0]]);
}); });
it('does run twice if activeAt less than one hour ', async () => { it('does run twice if activeAt less than one hour ', async () => {
@ -307,7 +307,7 @@ describe('SwarmPolling', () => {
swarmPolling.addGroupId(groupConvoPubkey); swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true); await swarmPolling.start(true);
expect(pollOnceForKeySpy.callCount).to.eq(2); expect(pollOnceForKeySpy.callCount).to.eq(2);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]); expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]);
pollOnceForKeySpy.resetHistory(); pollOnceForKeySpy.resetHistory();
clock.tick(9000); clock.tick(9000);
@ -322,7 +322,7 @@ describe('SwarmPolling', () => {
await sleepFor(10); await sleepFor(10);
expect(pollOnceForKeySpy.callCount).to.eq(2); expect(pollOnceForKeySpy.callCount).to.eq(2);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]); expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]);
}); });
@ -352,9 +352,9 @@ describe('SwarmPolling', () => {
await sleepFor(10); await sleepFor(10);
// we should have two more calls here, so 4 total. // we should have two more calls here, so 4 total.
expect(pollOnceForKeySpy.callCount).to.eq(4); expect(pollOnceForKeySpy.callCount).to.eq(4);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]); expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]);
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true, [-10]]); expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true, [-10]]);
}); });
@ -379,7 +379,7 @@ describe('SwarmPolling', () => {
// we should have only one more call here, the one for our direct pubkey fetch // we should have only one more call here, the one for our direct pubkey fetch
expect(pollOnceForKeySpy.callCount).to.eq(3); expect(pollOnceForKeySpy.callCount).to.eq(3);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]); // this one comes from the swarmPolling.start expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]); // this one comes from the swarmPolling.start
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, [0]]);
}); });
describe('multiple runs', () => { describe('multiple runs', () => {
@ -421,10 +421,10 @@ describe('SwarmPolling', () => {
// we have 4 calls total. 2 for our direct promises run each 5 seconds, and 2 for the group pubkey active (so run every 5 sec too) // we have 4 calls total. 2 for our direct promises run each 5 seconds, and 2 for the group pubkey active (so run every 5 sec too)
expect(pollOnceForKeySpy.callCount).to.eq(4); expect(pollOnceForKeySpy.callCount).to.eq(4);
// first two calls are our pubkey // first two calls are our pubkey
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]); expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]);
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true, [-10]]); expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true, [-10]]);
}); });
@ -445,9 +445,9 @@ describe('SwarmPolling', () => {
expect(pollOnceForKeySpy.callCount).to.eq(4); expect(pollOnceForKeySpy.callCount).to.eq(4);
// first two calls are our pubkey // first two calls are our pubkey
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]); expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]);
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]); expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true, [-10]]); expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true, [-10]]);
}); });
}); });

@ -76,8 +76,7 @@ async function checkIsFeatureReleased(featureName: FeatureNameTracked): Promise<
// Is it time to release the feature based on the network timestamp? // Is it time to release the feature based on the network timestamp?
if ( if (
!featureAlreadyReleased && !featureAlreadyReleased &&
(GetNetworkTime.getNowWithNetworkOffset() >= getFeatureReleaseTimestamp(featureName) || GetNetworkTime.getNowWithNetworkOffset() >= getFeatureReleaseTimestamp(featureName)
featureName === 'user_config_libsession') // we want to make a build which has user config enabled by default for internal testing // TODO to remove for official build
) { ) {
window.log.info(`[releaseFeature]: It is time to release ${featureName}. Releasing it now`); window.log.info(`[releaseFeature]: It is time to release ${featureName}. Releasing it now`);
await Storage.put(featureStorageItemId(featureName), true); await Storage.put(featureStorageItemId(featureName), true);

@ -141,7 +141,8 @@ async function verifySignature(
} }
const messageData = fromBase64ToUint8Array(messageBase64); const messageData = fromBase64ToUint8Array(messageBase64);
const signature = fromBase64ToUint8Array(signatureBase64); const signature = fromBase64ToUint8Array(signatureBase64);
const isBlindedSender = senderPubKey.startsWith('15'); // blinded15 or blinded25 are the same for the verifySignature logic
const isBlindedSender = senderPubKey.startsWith('15') || senderPubKey.startsWith('25');
const pubkeyWithoutPrefix = senderPubKey.slice(2); const pubkeyWithoutPrefix = senderPubKey.slice(2);
const pubkeyBytes = fromHexToArray(pubkeyWithoutPrefix); const pubkeyBytes = fromHexToArray(pubkeyWithoutPrefix);

@ -5155,9 +5155,9 @@ levn@~0.3.0:
prelude-ls "~1.1.2" prelude-ls "~1.1.2"
type-check "~0.3.2" type-check "~0.3.2"
"libsession_util_nodejs@https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.2.0/libsession_util_nodejs-v0.2.0.tar.gz": "libsession_util_nodejs@https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.2.2/libsession_util_nodejs-v0.2.2.tar.gz":
version "0.2.0" version "0.2.2"
resolved "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.2.0/libsession_util_nodejs-v0.2.0.tar.gz#0019ca2e3296b1e151dcd09923823c271dd2c0a0" resolved "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.2.2/libsession_util_nodejs-v0.2.2.tar.gz#129071c4d8f46c2ef25f364351284f76f51379ee"
dependencies: dependencies:
cmake-js "^7.2.1" cmake-js "^7.2.1"
node-addon-api "^6.1.0" node-addon-api "^6.1.0"

Loading…
Cancel
Save