fix: remove groups from protobuf

pull/2873/head
Audric Ackermann 2 years ago
parent 8c3b6508ad
commit 42913371df

@ -44,9 +44,6 @@ message SharedConfigMessage {
CONTACTS = 2;
CONVO_INFO_VOLATILE = 3;
USER_GROUPS = 4;
GROUP_INFO = 5;
GROUP_MEMBERS = 6;
GROUP_KEYS = 7;
}
required Kind kind = 1;

@ -34,7 +34,7 @@ import {
getGroupPubkeyFromWrapperType,
isUserConfigWrapperType,
} from '../../ts/webworker/workers/browser/libsession_worker_functions';
import { GroupConfigKind, UserConfigKind, isUserKind } from '../types/ProtobufKind';
import { UserConfigKind, isUserKind } from '../types/ProtobufKind';
import {
ContactsWrapperActions,
ConvoInfoVolatileWrapperActions,
@ -47,17 +47,14 @@ import { addKeyPairToCacheAndDBIfNeeded } from './closedGroups';
import { HexKeyPair } from './keypairs';
import { queueAllCachedFromSource } from './receiver';
type IncomingConfResult<T extends UserConfigKind | GroupConfigKind> = {
type IncomingUserResult = {
needsPush: boolean;
needsDump: boolean;
kind: T;
kind: UserConfigKind;
publicKey: string;
latestEnvelopeTimestamp: number;
};
type IncomingUserResult = IncomingConfResult<UserConfigKind>;
type IncomingGroupResult = IncomingConfResult<GroupConfigKind>;
function byUserVariant(
incomingConfigs: Array<IncomingMessage<SignalService.ISharedConfigMessage>>
) {

@ -1,7 +1,5 @@
import {
SharedGroupConfigMessage,
SharedUserConfigMessage,
} from '../../messages/outgoing/controlMessage/SharedConfigMessage';
import { GroupPubkeyType } from 'libsession_util_nodejs';
import { SharedUserConfigMessage } from '../../messages/outgoing/controlMessage/SharedConfigMessage';
import { SnodeNamespaces } from './namespaces';
export type SwarmForSubRequest = { method: 'get_swarm'; params: { pubkey: string } };
@ -112,7 +110,15 @@ export type StoreOnNodeMessage = {
pubkey: string;
timestamp: number;
namespace: number;
message: SharedUserConfigMessage | SharedGroupConfigMessage;
message: SharedUserConfigMessage;
};
export type StoreOnNodeData = {
pubkey: GroupPubkeyType;
networkTimestamp: number;
namespace: number;
data: Uint8Array;
ttl: number;
};
export type StoreOnNodeSubRequest = { method: 'store'; params: StoreOnNodeParams };

@ -5,17 +5,16 @@ import { SignalService } from '../../../../protobuf';
import { MessageParams } from '../Message';
import { ContentMessage } from '..';
import { TTL_DEFAULT } from '../../../constants';
import { GroupConfigKind, UserConfigKind } from '../../../../types/ProtobufKind';
import { UserConfigKind } from '../../../../types/ProtobufKind';
interface SharedConfigParams<KindsPicked extends UserConfigKind | GroupConfigKind>
extends MessageParams {
interface SharedConfigParams<KindsPicked extends UserConfigKind> extends MessageParams {
seqno: Long;
data: Uint8Array;
kind: KindsPicked;
}
export abstract class SharedConfigMessage<
KindsPicked extends UserConfigKind | GroupConfigKind
KindsPicked extends UserConfigKind
> extends ContentMessage {
public readonly seqno: Long;
public readonly kind: KindsPicked;
@ -58,15 +57,3 @@ export class SharedUserConfigMessage extends SharedConfigMessage<UserConfigKind>
});
}
}
export class SharedGroupConfigMessage extends SharedConfigMessage<GroupConfigKind> {
constructor(params: SharedConfigParams<GroupConfigKind>) {
super({
timestamp: params.timestamp,
identifier: params.identifier,
data: params.data,
kind: params.kind,
seqno: params.seqno,
});
}
}

@ -233,12 +233,7 @@ export class MessageQueue {
pubkey,
}: {
pubkey: PubKey;
message:
| ClosedGroupNewMessage
| CallMessage
// | SharedUserConfigMessage
// | SharedGroupConfigMessage
| ClosedGroupMemberLeftMessage;
message: ClosedGroupNewMessage | CallMessage | ClosedGroupMemberLeftMessage;
namespace: SnodeNamespaces;
}): Promise<boolean | number> {
let rawMessage;

@ -17,6 +17,7 @@ import { SnodeNamespace, SnodeNamespaces } from '../apis/snode_api/namespaces';
import { getSwarmFor } from '../apis/snode_api/snodePool';
import {
NotEmptyArrayOfBatchResults,
StoreOnNodeData,
StoreOnNodeMessage,
StoreOnNodeParams,
StoreOnNodeParamsNoSig,
@ -37,6 +38,8 @@ import { PubKey } from '../types';
import { RawMessage } from '../types/RawMessage';
import { EmptySwarmError } from '../utils/errors';
import { fromUInt8ArrayToBase64 } from '../utils/String';
import { GroupPubkeyType } from 'libsession_util_nodejs';
import { to_base64 } from 'libsodium-wrappers-sumo';
// ================ SNODE STORE ================
@ -446,6 +449,53 @@ async function sendMessagesToSnode(
}
}
/**
* Send an array of preencrypted data to the corresponding swarm.
* Used currently only for sending libsession GroupInfo, GroupMembers and groupKeys config updates.
*
* @param params the data to deposit
* @param destination the pubkey we should deposit those message to
* @returns the hashes of successful deposit
*/
async function sendEncryptedDataToSnode(
encryptedData: Array<StoreOnNodeData>,
destination: GroupPubkeyType,
messagesHashesToDelete: Set<string> | null
): Promise<NotEmptyArrayOfBatchResults | null> {
try {
const batchResults = await pRetry(
async () => {
return MessageSender.sendMessagesDataToSnode(
encryptedData.map(content => ({
pubkey: destination,
data64: to_base64(content.data),
ttl: content.ttl,
timestamp: content.networkTimestamp,
namespace: content.namespace,
})),
destination,
messagesHashesToDelete
);
},
{
retries: 2,
factor: 1,
minTimeout: MessageSender.getMinRetryTimeout(),
maxTimeout: 1000,
}
);
if (!batchResults || isEmpty(batchResults)) {
throw new Error('result is empty for sendEncryptedDataToSnode');
}
return batchResults;
} catch (e) {
window.log.warn(`sendEncryptedDataToSnode failed with ${e.message}`);
return null;
}
}
async function buildEnvelope(
type: SignalService.Envelope.Type,
sskSource: string | undefined,
@ -543,6 +593,7 @@ export const MessageSender = {
sendToOpenGroupV2BlindedRequest,
sendMessagesDataToSnode,
sendMessagesToSnode,
sendEncryptedDataToSnode,
getMinRetryTimeout,
sendToOpenGroupV2,
send,

@ -31,7 +31,7 @@ const defaultMaxAttempts = 2;
*/
let lastRunConfigSyncJobTimestamp: number | null = null;
export type SingleDestinationChanges = {
type SingleDestinationChanges = {
messages: Array<OutgoingConfResult<UserConfigKind, SharedUserConfigMessage>>;
allOldHashes: Array<string>;
};

@ -5,14 +5,19 @@ import { UserUtils } from '../..';
import { ConfigDumpData } from '../../../../data/configDump/configDump';
import { ReleasedFeatures } from '../../../../util/releaseFeature';
import { isSignInByLinking } from '../../../../util/storage';
import { isMetaWrapperType } from '../../../../webworker/workers/browser/libsession_worker_functions';
import { NotEmptyArrayOfBatchResults } from '../../../apis/snode_api/SnodeRequestTypes';
import { MetaGroupWrapperActions } from '../../../../webworker/workers/browser/libsession_worker_interface';
import {
NotEmptyArrayOfBatchResults,
StoreOnNodeData,
} from '../../../apis/snode_api/SnodeRequestTypes';
import { GetNetworkTime } from '../../../apis/snode_api/getNetworkTime';
import { SnodeNamespaces } from '../../../apis/snode_api/namespaces';
import { TTL_DEFAULT } from '../../../constants';
import { getConversationController } from '../../../conversations';
import { SharedGroupConfigMessage } from '../../../messages/outgoing/controlMessage/SharedConfigMessage';
import { MessageSender } from '../../../sending/MessageSender';
import { PubKey } from '../../../types';
import { allowOnlyOneAtATime } from '../../Promise';
import { LibSessionUtil, OutgoingConfResult } from '../../libsession/libsession_utils';
import { LibSessionUtil, PendingChangesForGroup } from '../../libsession/libsession_utils';
import { runners } from '../JobRunner';
import {
AddJobCheckReturn,
@ -20,9 +25,6 @@ import {
PersistedJob,
RunJobResult,
} from '../PersistedJob';
import { MetaGroupWrapperActions } from '../../../../webworker/workers/browser/libsession_worker_interface';
import { SignalService } from '../../../../protobuf';
import { GroupConfigKind } from '../../../../types/ProtobufKind';
const defaultMsBetweenRetries = 15000; // a long time between retries, to avoid running multiple jobs at the same time, when one was postponed at the same time as one already planned (5s)
const defaultMaxAttempts = 2;
@ -33,13 +35,13 @@ const defaultMaxAttempts = 2;
*/
let lastRunConfigSyncJobTimestamp: number | null = null;
export type SingleDestinationChanges = {
messages: Array<OutgoingConfResult<GroupConfigKind, SharedGroupConfigMessage>>;
type GroupSingleDestinationChanges = {
messages: Array<PendingChangesForGroup>;
allOldHashes: Array<string>;
};
type SuccessfulChange = {
message: SharedGroupConfigMessage;
pushed: PendingChangesForGroup;
updatedHash: string;
};
@ -47,14 +49,24 @@ type SuccessfulChange = {
* Later in the syncing logic, we want to batch-send all the updates for a pubkey in a single batch call.
* To make this easier, this function prebuilds and merges together all the changes for each pubkey.
*/
async function retrieveSingleDestinationChanges(
async function retrieveGroupSingleDestinationChanges(
groupPk: GroupPubkeyType
): Promise<SingleDestinationChanges> {
): Promise<GroupSingleDestinationChanges> {
const outgoingConfResults = await LibSessionUtil.pendingChangesForGroup(groupPk);
const compactedHashes = compact(outgoingConfResults.map(m => m.oldMessageHashes)).flat();
return { messages: outgoingConfResults, allOldHashes: compactedHashes };
const compactedHashes = compact([...outgoingConfResults].map(m => m[1].oldMessageHashes)).flat();
const sortedMessagesKeyFirst = compact(
[...outgoingConfResults.keys()]
.sort((a, b) => {
if (a === 'GroupKeys') return -1;
if (b === 'GroupKeys') return 1;
return 0;
})
.map(key => {
return outgoingConfResults.get(key);
})
);
return { messages: sortedMessagesKeyFirst, allOldHashes: compactedHashes };
}
/**
@ -62,7 +74,7 @@ async function retrieveSingleDestinationChanges(
*/
function resultsToSuccessfulChange(
result: NotEmptyArrayOfBatchResults | null,
request: SingleDestinationChanges
request: GroupSingleDestinationChanges
): Array<SuccessfulChange> {
const successfulChanges: Array<SuccessfulChange> = [];
@ -82,16 +94,13 @@ function resultsToSuccessfulChange(
for (let j = 0; j < result.length; j++) {
const batchResult = result[j];
const messagePostedHashes = batchResult?.body?.hash;
console.warn('messagePostedHashes', messagePostedHashes);
if (
batchResult.code === 200 &&
isString(messagePostedHashes) &&
request.messages?.[j].message
) {
// the library keeps track of the hashes to push and pushed using the hashes now
if (batchResult.code === 200 && isString(messagePostedHashes) && request.messages?.[j].data) {
// libsession keeps track of the hashes to push and pushed using the hashes now
successfulChanges.push({
updatedHash: messagePostedHashes,
message: request.messages?.[j].message,
pushed: request.messages?.[j],
});
}
}
@ -110,27 +119,23 @@ async function buildAndSaveDumpsToDB(
for (let i = 0; i < changes.length; i++) {
const change = changes[i];
const variant = LibSessionUtil.groupKindToVariant(change.message.kind, groupPk);
if (!isMetaWrapperType(variant)) {
throw new Error(`buildAndSaveDumpsToDB non metagroup variant: ${variant}`);
}
const Kind = SignalService.SharedConfigMessage.Kind;
switch (change.message.kind) {
case Kind.GROUP_INFO: {
toConfirm[1].groupInfo = [change.message.seqno.toNumber(), change.updatedHash];
switch (change.pushed.namespace) {
case SnodeNamespaces.ClosedGroupInfo: {
toConfirm[1].groupInfo = [change.pushed.seqno.toNumber(), change.updatedHash];
break;
}
case Kind.GROUP_MEMBERS: {
toConfirm[1].groupMember = [change.message.seqno.toNumber(), change.updatedHash];
case SnodeNamespaces.ClosedGroupMembers: {
toConfirm[1].groupMember = [change.pushed.seqno.toNumber(), change.updatedHash];
break;
}
case Kind.GROUP_KEYS: {
toConfirm[1].groupKeys = [change.message.seqno.toNumber(), change.updatedHash];
case SnodeNamespaces.ClosedGroupKeys: {
toConfirm[1].groupKeys = [change.pushed.seqno.toNumber(), change.updatedHash];
break;
}
}
}
await MetaGroupWrapperActions.metaConfirmPushed(...toConfirm);
const metaNeedsDump = await MetaGroupWrapperActions.needsDump(groupPk);
// save the concatenated dumps as a single entry in the DB if any of the dumps had a need for dump
@ -207,7 +212,7 @@ class GroupSyncJob extends PersistedJob<GroupSyncPersistedData> {
if (!newGroupsReleased) {
return RunJobResult.Success;
}
const singleDestChanges = await retrieveSingleDestinationChanges(thisJobDestination);
const singleDestChanges = await retrieveGroupSingleDestinationChanges(thisJobDestination);
// If there are no pending changes then the job can just complete (next time something
// is updated we want to try and run immediately so don't scuedule another run in this case)
@ -215,17 +220,18 @@ class GroupSyncJob extends PersistedJob<GroupSyncPersistedData> {
return RunJobResult.Success;
}
const oldHashesToDelete = new Set(singleDestChanges.allOldHashes);
const msgs = singleDestChanges.messages.map(item => {
const msgs: Array<StoreOnNodeData> = singleDestChanges.messages.map(item => {
return {
namespace: item.namespace,
pubkey: thisJobDestination,
timestamp: item.message.timestamp,
ttl: item.message.ttl(),
message: item.message,
networkTimestamp: GetNetworkTime.getNowWithNetworkOffset(),
ttl: TTL_DEFAULT.TTL_CONFIG,
data: item.data,
};
});
const result = await MessageSender.sendMessagesToSnode(
const result = await MessageSender.sendEncryptedDataToSnode(
msgs,
thisJobDestination,
oldHashesToDelete
@ -236,7 +242,7 @@ class GroupSyncJob extends PersistedJob<GroupSyncPersistedData> {
// we do a sequence call here. If we do not have the right expected number of results, consider it a failure
if (!isArray(result) || result.length !== expectedReplyLength) {
window.log.info(
`ConfigurationSyncJob: unexpected result length: expected ${expectedReplyLength} but got ${result?.length}`
`GroupSyncJob: unexpected result length: expected ${expectedReplyLength} but got ${result?.length}`
);
// this might be a 421 error (already handled) so let's retry this request a little bit later
return RunJobResult.RetryJobIfPossible;

@ -10,7 +10,6 @@ import { HexString } from '../../../node/hexStrings';
import { SignalService } from '../../../protobuf';
import { assertUnreachable, toFixedUint8ArrayOfLength } from '../../../types/sqlSharedTypes';
import {
ConfigWrapperGroup,
ConfigWrapperGroupDetailed,
ConfigWrapperUser,
getGroupPubkeyFromWrapperType,
@ -26,13 +25,12 @@ import { GetNetworkTime } from '../../apis/snode_api/getNetworkTime';
import { SnodeNamespaces } from '../../apis/snode_api/namespaces';
import {
SharedConfigMessage,
SharedGroupConfigMessage,
SharedUserConfigMessage,
} from '../../messages/outgoing/controlMessage/SharedConfigMessage';
import { PubKey } from '../../types';
import { getUserED25519KeyPairBytes } from '../User';
import { ConfigurationSync } from '../job_runners/jobs/ConfigurationSyncJob';
import { GroupConfigKind, UserConfigKind } from '../../../types/ProtobufKind';
import { UserConfigKind } from '../../../types/ProtobufKind';
const requiredUserVariants: Array<ConfigWrapperUser> = [
'UserConfig',
@ -41,10 +39,7 @@ const requiredUserVariants: Array<ConfigWrapperUser> = [
'ConvoInfoVolatileConfig',
];
export type OutgoingConfResult<
K extends UserConfigKind | GroupConfigKind,
T extends SharedConfigMessage<K>
> = {
export type OutgoingConfResult<K extends UserConfigKind, T extends SharedConfigMessage<K>> = {
message: T;
namespace: SnodeNamespaces;
oldMessageHashes: Array<string>;
@ -203,60 +198,53 @@ async function pendingChangesForUs(): Promise<
return results;
}
export type PendingChangesForGroup = {
data: Uint8Array;
seqno: Long;
timestamp: number;
oldMessageHashes: Array<string>;
namespace: SnodeNamespaces;
};
async function pendingChangesForGroup(
groupPk: GroupPubkeyType
): Promise<Array<OutgoingConfResult<GroupConfigKind, SharedGroupConfigMessage>>> {
const dumps = await ConfigDumpData.getAllDumpsWithoutDataFor(groupPk);
const results: Array<OutgoingConfResult<GroupConfigKind, SharedGroupConfigMessage>> = [];
): Promise<Map<ConfigWrapperGroupDetailed, PendingChangesForGroup>> {
const results: Map<ConfigWrapperGroupDetailed, PendingChangesForGroup> = new Map();
const variantsNeedingPush = new Set<ConfigWrapperGroupDetailed>();
if (!PubKey.isClosedGroupV3(groupPk)) {
throw new Error(`pendingChangesForGroup only works for user or 03 group pubkeys`);
}
for (let index = 0; index < dumps.length; index++) {
const dump = dumps[index];
const variant = dump.variant;
if (!isMetaWrapperType(variant)) {
// shouldn't happen
continue;
}
// one of the wrapper behind the metagroup needs a push
const needsPush = await MetaGroupWrapperActions.needsPush(groupPk);
// one of the wrapper behind the metagroup needs a push
const needsPush = await MetaGroupWrapperActions.needsPush(groupPk);
// we probably need to add the GROUP_KEYS check here
if (!needsPush) {
continue;
}
if (!needsPush) {
return results;
}
const { groupInfo, groupMember } = await MetaGroupWrapperActions.push(groupPk);
if (groupInfo) {
variantsNeedingPush.add('GroupInfo');
results.push({
message: new SharedGroupConfigMessage({
data: groupInfo.data,
kind: SignalService.SharedConfigMessage.Kind.GROUP_INFO,
seqno: Long.fromNumber(groupInfo.seqno),
timestamp: GetNetworkTime.getNowWithNetworkOffset(),
}),
oldMessageHashes: groupInfo.hashes,
namespace: groupInfo.namespace,
});
}
if (groupMember) {
variantsNeedingPush.add('GroupMember');
results.push({
message: new SharedGroupConfigMessage({
data: groupMember.data,
kind: SignalService.SharedConfigMessage.Kind.GROUP_MEMBERS,
seqno: Long.fromNumber(groupMember.seqno),
timestamp: GetNetworkTime.getNowWithNetworkOffset(),
}),
oldMessageHashes: groupMember.hashes,
namespace: groupMember.namespace,
});
}
const { groupInfo, groupMember } = await MetaGroupWrapperActions.push(groupPk);
if (groupInfo) {
variantsNeedingPush.add('GroupInfo');
results.set('GroupInfo', {
data: groupInfo.data,
seqno: Long.fromNumber(groupInfo.seqno),
timestamp: GetNetworkTime.getNowWithNetworkOffset(),
oldMessageHashes: groupInfo.hashes,
namespace: groupInfo.namespace,
});
}
if (groupMember) {
variantsNeedingPush.add('GroupMember');
results.set('GroupMember', {
data: groupMember.data,
seqno: Long.fromNumber(groupMember.seqno),
timestamp: GetNetworkTime.getNowWithNetworkOffset(),
oldMessageHashes: groupMember.hashes,
namespace: groupMember.namespace,
});
}
window.log.info(`those variants needs push: "${[...variantsNeedingPush]}"`);
@ -293,20 +281,6 @@ function userVariantToUserKind(variant: ConfigWrapperUser) {
}
}
function groupKindToVariant(kind: GroupConfigKind, groupPk: GroupPubkeyType): ConfigWrapperGroup {
if (!PubKey.isClosedGroupV3(groupPk)) {
throw new Error(`Not a groupPk starting with 03: ${groupPk}`);
}
switch (kind) {
case SignalService.SharedConfigMessage.Kind.GROUP_INFO:
case SignalService.SharedConfigMessage.Kind.GROUP_KEYS:
case SignalService.SharedConfigMessage.Kind.GROUP_MEMBERS:
return `MetaGroupConfig-${groupPk}`;
default:
assertUnreachable(kind, `userKindToVariant: Unsupported variant: "${kind}"`);
}
}
/**
* Returns true if the config needs to be dumped afterwards
*/
@ -322,6 +296,5 @@ export const LibSessionUtil = {
pendingChangesForUs,
pendingChangesForGroup,
userKindToVariant,
groupKindToVariant,
markAsPushed,
};

@ -18,15 +18,3 @@ export function isUserKind(kind: SignalService.SharedConfigMessage.Kind): kind i
kind === Kind.CONVO_INFO_VOLATILE
);
}
export type GroupConfigKind = PickEnum<
SignalService.SharedConfigMessage.Kind,
| SignalService.SharedConfigMessage.Kind.GROUP_INFO
| SignalService.SharedConfigMessage.Kind.GROUP_MEMBERS
| SignalService.SharedConfigMessage.Kind.GROUP_KEYS
>;
export function isGroupKind(kind: SignalService.SharedConfigMessage.Kind): kind is GroupConfigKind {
const Kind = SignalService.SharedConfigMessage.Kind;
return kind === Kind.GROUP_INFO || kind === Kind.GROUP_MEMBERS || kind === Kind.GROUP_KEYS;
}

Loading…
Cancel
Save