added hf switching of poll&store requests + tests

pull/2290/head
Audric Ackermann 3 years ago
parent 363977b358
commit d948045e6a
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -15,7 +15,6 @@ export async function getHasSeenHF190() {
if (hasSeenHardfork190 === undefined) { if (hasSeenHardfork190 === undefined) {
// read values from db and cache them as it looks like we did not // read values from db and cache them as it looks like we did not
const oldHhasSeenHardfork190 = (await getItemById('hasSeenHardfork190'))?.value; const oldHhasSeenHardfork190 = (await getItemById('hasSeenHardfork190'))?.value;
// values do not exist in the db yet. Let's store false for now in the db and update our cached value. // values do not exist in the db yet. Let's store false for now in the db and update our cached value.
if (oldHhasSeenHardfork190 === undefined) { if (oldHhasSeenHardfork190 === undefined) {
await createOrUpdateItem({ id: 'hasSeenHardfork190', value: false }); await createOrUpdateItem({ id: 'hasSeenHardfork190', value: false });

@ -3,7 +3,7 @@ import * as snodePool from './snodePool';
import { ERROR_CODE_NO_CONNECT, retrieveNextMessages } from './SNodeAPI'; import { ERROR_CODE_NO_CONNECT, retrieveNextMessages } from './SNodeAPI';
import { SignalService } from '../../../protobuf'; import { SignalService } from '../../../protobuf';
import * as Receiver from '../../../receiver/receiver'; import * as Receiver from '../../../receiver/receiver';
import _ from 'lodash'; import _, { concat } from 'lodash';
import { import {
getLastHashBySnode, getLastHashBySnode,
getSeenMessagesByHashList, getSeenMessagesByHashList,
@ -20,6 +20,7 @@ import { perfEnd, perfStart } from '../../utils/Performance';
import { ed25519Str } from '../../onions/onionPath'; import { ed25519Str } from '../../onions/onionPath';
import { updateIsOnline } from '../../../state/ducks/onion'; import { updateIsOnline } from '../../../state/ducks/onion';
import pRetry from 'p-retry'; import pRetry from 'p-retry';
import { getHasSeenHF190, getHasSeenHF191 } from './hfHandling';
interface Message { interface Message {
hash: string; hash: string;
@ -145,7 +146,10 @@ export class SwarmPolling {
} }
// we always poll as often as possible for our pubkey // we always poll as often as possible for our pubkey
const ourPubkey = UserUtils.getOurPubKeyFromCache(); const ourPubkey = UserUtils.getOurPubKeyFromCache();
const directPromise = this.pollOnceForKey(ourPubkey, false, 0); const directPromises = Promise.all([
this.pollOnceForKey(ourPubkey, false, 0),
// this.pollOnceForKey(ourPubkey, false, 5), // uncomment, and test me once we store the config messages to the namespace 5
]).then(() => undefined);
const now = Date.now(); const now = Date.now();
const groupPromises = this.groupPolling.map(async group => { const groupPromises = this.groupPolling.map(async group => {
@ -162,6 +166,25 @@ export class SwarmPolling {
window?.log?.info( window?.log?.info(
`Polling for ${loggingId}; timeout: ${convoPollingTimeout} ; diff: ${diff}` `Polling for ${loggingId}; timeout: ${convoPollingTimeout} ; diff: ${diff}`
); );
const hardfork190Happened = await getHasSeenHF190();
const hardfork191Happened = await getHasSeenHF191();
if (hardfork190Happened && !hardfork191Happened) {
// during the transition period, we poll from both namespaces (0 and -10) for groups
return Promise.all([
this.pollOnceForKey(group.pubkey, true, 0),
this.pollOnceForKey(group.pubkey, true, -10),
]).then(() => undefined);
}
if (hardfork190Happened && hardfork191Happened) {
// after the transition period, we poll from the namespace -10 only for groups
return this.pollOnceForKey(group.pubkey, true, -10);
}
// before any of those hardforks, we just poll from the default namespace being 0
console.warn('before any of those hardforks');
return this.pollOnceForKey(group.pubkey, true, 0); return this.pollOnceForKey(group.pubkey, true, 0);
} }
window?.log?.info( window?.log?.info(
@ -171,7 +194,7 @@ export class SwarmPolling {
return Promise.resolve(); return Promise.resolve();
}); });
try { try {
await Promise.all(_.concat(directPromise, groupPromises)); await Promise.all(concat([directPromises], groupPromises));
} catch (e) { } catch (e) {
window?.log?.info('pollForAllKeys exception: ', e); window?.log?.info('pollForAllKeys exception: ', e);
throw e; throw e;
@ -193,22 +216,19 @@ export class SwarmPolling {
// If we need more nodes, select randomly from the remaining nodes: // If we need more nodes, select randomly from the remaining nodes:
// Use 1 node for now: // We only poll from a single node.
const COUNT = 1; let nodesToPoll = _.sampleSize(alreadyPolled, 1);
if (nodesToPoll.length < 1) {
let nodesToPoll = _.sampleSize(alreadyPolled, COUNT);
if (nodesToPoll.length < COUNT) {
const notPolled = _.difference(swarmSnodes, alreadyPolled); const notPolled = _.difference(swarmSnodes, alreadyPolled);
const newNeeded = COUNT - alreadyPolled.length; const newNodes = _.sampleSize(notPolled, 1);
const newNodes = _.sampleSize(notPolled, newNeeded);
nodesToPoll = _.concat(nodesToPoll, newNodes); nodesToPoll = _.concat(nodesToPoll, newNodes);
} }
// this actually doesn't make much sense as we are at only polling from a single one
const promisesSettled = await Promise.allSettled( const promisesSettled = await Promise.allSettled(
nodesToPoll.map(async (n: Snode) => { nodesToPoll.map(async n => {
return this.pollNodeForKey(n, pubkey, namespace); return this.pollNodeForKey(n, pubkey, namespace);
}) })
); );

@ -1,4 +1,3 @@
import { EncryptionType } from '../types/EncryptionType';
import { SignalService } from '../../protobuf'; import { SignalService } from '../../protobuf';
import { PubKey } from '../types'; import { PubKey } from '../types';
import { concatUInt8Array, getSodiumRenderer, MessageEncrypter } from '.'; import { concatUInt8Array, getSodiumRenderer, MessageEncrypter } from '.';
@ -24,15 +23,15 @@ type EncryptResult = {
export async function encrypt( export async function encrypt(
device: PubKey, device: PubKey,
plainTextBuffer: Uint8Array, plainTextBuffer: Uint8Array,
encryptionType: EncryptionType encryptionType: SignalService.Envelope.Type
): Promise<EncryptResult> { ): Promise<EncryptResult> {
const { CLOSED_GROUP_MESSAGE, SESSION_MESSAGE } = SignalService.Envelope.Type; const { CLOSED_GROUP_MESSAGE, SESSION_MESSAGE } = SignalService.Envelope.Type;
if (encryptionType !== EncryptionType.ClosedGroup && encryptionType !== EncryptionType.Fallback) { if (encryptionType !== CLOSED_GROUP_MESSAGE && encryptionType !== SESSION_MESSAGE) {
throw new Error(`Invalid encryption type:${encryptionType}`); throw new Error(`Invalid encryption type:${encryptionType}`);
} }
const encryptForClosedGroup = encryptionType === EncryptionType.ClosedGroup; const encryptForClosedGroup = encryptionType === CLOSED_GROUP_MESSAGE;
const plainText = addMessagePadding(plainTextBuffer); const plainText = addMessagePadding(plainTextBuffer);
if (encryptForClosedGroup) { if (encryptForClosedGroup) {

@ -75,7 +75,7 @@ export async function send(
): Promise<{ wrappedEnvelope: Uint8Array; effectiveTimestamp: number }> { ): Promise<{ wrappedEnvelope: Uint8Array; effectiveTimestamp: number }> {
return pRetry( return pRetry(
async () => { async () => {
const device = PubKey.cast(message.device); const recipient = PubKey.cast(message.device);
const { encryption, ttl } = message; const { encryption, ttl } = message;
const { const {
@ -84,12 +84,17 @@ export async function send(
} = overwriteOutgoingTimestampWithNetworkTimestamp(message); } = overwriteOutgoingTimestampWithNetworkTimestamp(message);
const { envelopeType, cipherText } = await MessageEncrypter.encrypt( const { envelopeType, cipherText } = await MessageEncrypter.encrypt(
device, recipient,
overRiddenTimestampBuffer, overRiddenTimestampBuffer,
encryption encryption
); );
const envelope = await buildEnvelope(envelopeType, device.key, networkTimestamp, cipherText); const envelope = await buildEnvelope(
envelopeType,
recipient.key,
networkTimestamp,
cipherText
);
const data = wrapEnvelope(envelope); const data = wrapEnvelope(envelope);
// make sure to update the local sent_at timestamp, because sometimes, we will get the just pushed message in the receiver side // make sure to update the local sent_at timestamp, because sometimes, we will get the just pushed message in the receiver side
@ -102,8 +107,8 @@ export async function send(
found.set({ sent_at: networkTimestamp }); found.set({ sent_at: networkTimestamp });
await found.commit(); await found.commit();
} }
await MessageSender.TEST_sendMessageToSnode( await MessageSender.sendMessageToSnode(
device.key, recipient.key,
data, data,
ttl, ttl,
networkTimestamp, networkTimestamp,
@ -121,7 +126,7 @@ export async function send(
} }
// tslint:disable-next-line: function-name // tslint:disable-next-line: function-name
export async function TEST_sendMessageToSnode( export async function sendMessageToSnode(
pubKey: string, pubKey: string,
data: Uint8Array, data: Uint8Array,
ttl: number, ttl: number,

@ -3,7 +3,7 @@ import { getMessageById } from '../../data/data';
import { SignalService } from '../../protobuf'; import { SignalService } from '../../protobuf';
import { PnServer } from '../apis/push_notification_api'; import { PnServer } from '../apis/push_notification_api';
import { OpenGroupVisibleMessage } from '../messages/outgoing/visibleMessage/OpenGroupVisibleMessage'; import { OpenGroupVisibleMessage } from '../messages/outgoing/visibleMessage/OpenGroupVisibleMessage';
import { EncryptionType, RawMessage } from '../types'; import { RawMessage } from '../types';
import { UserUtils } from '../utils'; import { UserUtils } from '../utils';
// tslint:disable-next-line: no-unnecessary-class // tslint:disable-next-line: no-unnecessary-class
@ -58,7 +58,8 @@ export class MessageSentHandler {
// FIXME this is not correct and will cause issues with syncing // FIXME this is not correct and will cause issues with syncing
// At this point the only way to check for medium // At this point the only way to check for medium
// group is by comparing the encryption type // group is by comparing the encryption type
const isClosedGroupMessage = sentMessage.encryption === EncryptionType.ClosedGroup; const isClosedGroupMessage =
sentMessage.encryption === SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE;
// We trigger a sync message only when the message is not to one of our devices, AND // We trigger a sync message only when the message is not to one of our devices, AND
// the message is not for an open group (there is no sync for opengroups, each device pulls all messages), AND // the message is not for an open group (there is no sync for opengroups, each device pulls all messages), AND

@ -1,5 +0,0 @@
export enum EncryptionType {
Signal,
Fallback,
ClosedGroup,
}

@ -1,11 +1,11 @@
import { EncryptionType } from './EncryptionType'; import { SignalService } from '../../protobuf';
export type RawMessage = { export type RawMessage = {
identifier: string; identifier: string;
plainTextBuffer: Uint8Array; plainTextBuffer: Uint8Array;
device: string; device: string;
ttl: number; ttl: number;
encryption: EncryptionType; encryption: SignalService.Envelope.Type;
}; };
// For building RawMessages from JSON // For building RawMessages from JSON

@ -1,3 +1,2 @@
export * from './EncryptionType';
export * from './RawMessage'; export * from './RawMessage';
export * from './PubKey'; export * from './PubKey';

@ -1,22 +1,23 @@
import { RawMessage } from '../types/RawMessage'; import { RawMessage } from '../types/RawMessage';
import { EncryptionType, PubKey } from '../types'; import { PubKey } from '../types';
import { ClosedGroupMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupMessage'; import { ClosedGroupMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupMessage';
import { ClosedGroupNewMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupNewMessage'; import { ClosedGroupNewMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupNewMessage';
import { ClosedGroupEncryptionPairReplyMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupEncryptionPairReplyMessage'; import { ClosedGroupEncryptionPairReplyMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupEncryptionPairReplyMessage';
import { ContentMessage } from '../messages/outgoing'; import { ContentMessage } from '../messages/outgoing';
import { ExpirationTimerUpdateMessage } from '../messages/outgoing/controlMessage/ExpirationTimerUpdateMessage'; import { ExpirationTimerUpdateMessage } from '../messages/outgoing/controlMessage/ExpirationTimerUpdateMessage';
import { SignalService } from '../../protobuf';
function getEncryptionTypeFromMessageType( function getEncryptionTypeFromMessageType(
message: ContentMessage, message: ContentMessage,
isGroup = false isGroup = false
): EncryptionType { ): SignalService.Envelope.Type {
// ClosedGroupNewMessage is sent using established channels, so using fallback // ClosedGroupNewMessage is sent using established channels, so using fallback
if ( if (
message instanceof ClosedGroupNewMessage || message instanceof ClosedGroupNewMessage ||
message instanceof ClosedGroupEncryptionPairReplyMessage message instanceof ClosedGroupEncryptionPairReplyMessage
) { ) {
return EncryptionType.Fallback; return SignalService.Envelope.Type.SESSION_MESSAGE;
} }
// 1. any ClosedGroupMessage which is not a ClosedGroupNewMessage must be encoded with ClosedGroup // 1. any ClosedGroupMessage which is not a ClosedGroupNewMessage must be encoded with ClosedGroup
@ -26,9 +27,9 @@ function getEncryptionTypeFromMessageType(
(message instanceof ExpirationTimerUpdateMessage && message.groupId) || (message instanceof ExpirationTimerUpdateMessage && message.groupId) ||
isGroup isGroup
) { ) {
return EncryptionType.ClosedGroup; return SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE;
} else { } else {
return EncryptionType.Fallback; return SignalService.Envelope.Type.SESSION_MESSAGE;
} }
} }

@ -2,7 +2,6 @@ import chai, { expect } from 'chai';
import * as crypto from 'crypto'; import * as crypto from 'crypto';
import Sinon, * as sinon from 'sinon'; import Sinon, * as sinon from 'sinon';
import { concatUInt8Array, getSodiumRenderer, MessageEncrypter } from '../../../../session/crypto'; import { concatUInt8Array, getSodiumRenderer, MessageEncrypter } from '../../../../session/crypto';
import { EncryptionType } from '../../../../session/types/EncryptionType';
import { TestUtils } from '../../../test-utils'; import { TestUtils } from '../../../test-utils';
import { SignalService } from '../../../../protobuf'; import { SignalService } from '../../../../protobuf';
@ -120,7 +119,7 @@ describe('MessageEncrypter', () => {
const result = await MessageEncrypter.encrypt( const result = await MessageEncrypter.encrypt(
TestUtils.generateFakePubKey(), TestUtils.generateFakePubKey(),
data, data,
EncryptionType.ClosedGroup SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE
); );
chai chai
.expect(result.envelopeType) .expect(result.envelopeType)
@ -133,7 +132,7 @@ describe('MessageEncrypter', () => {
const result = await MessageEncrypter.encrypt( const result = await MessageEncrypter.encrypt(
TestUtils.generateFakePubKey(), TestUtils.generateFakePubKey(),
data, data,
EncryptionType.Fallback SignalService.Envelope.Type.SESSION_MESSAGE
); );
chai.expect(result.envelopeType).to.deep.equal(SignalService.Envelope.Type.SESSION_MESSAGE); chai.expect(result.envelopeType).to.deep.equal(SignalService.Envelope.Type.SESSION_MESSAGE);
}); });
@ -143,7 +142,7 @@ describe('MessageEncrypter', () => {
return MessageEncrypter.encrypt( return MessageEncrypter.encrypt(
TestUtils.generateFakePubKey(), TestUtils.generateFakePubKey(),
data, data,
EncryptionType.Signal 3 as any
).should.eventually.be.rejectedWith(Error); ).should.eventually.be.rejectedWith(Error);
}); });
}); });
@ -162,7 +161,11 @@ describe('MessageEncrypter', () => {
it('should pass the padded message body to encrypt', async () => { it('should pass the padded message body to encrypt', async () => {
const data = crypto.randomBytes(10); const data = crypto.randomBytes(10);
const spy = sinon.spy(MessageEncrypter, 'encryptUsingSessionProtocol'); const spy = sinon.spy(MessageEncrypter, 'encryptUsingSessionProtocol');
await MessageEncrypter.encrypt(TestUtils.generateFakePubKey(), data, EncryptionType.Fallback); await MessageEncrypter.encrypt(
TestUtils.generateFakePubKey(),
data,
SignalService.Envelope.Type.SESSION_MESSAGE
);
chai.expect(spy.callCount).to.be.equal(1); chai.expect(spy.callCount).to.be.equal(1);
const paddedData = addMessagePadding(data); const paddedData = addMessagePadding(data);
const firstArgument = new Uint8Array(spy.args[0][1]); const firstArgument = new Uint8Array(spy.args[0][1]);

@ -5,7 +5,6 @@ import { MessageSender } from '../../../../session/sending';
import { TestUtils } from '../../../test-utils'; import { TestUtils } from '../../../test-utils';
import { MessageEncrypter } from '../../../../session/crypto'; import { MessageEncrypter } from '../../../../session/crypto';
import { SignalService } from '../../../../protobuf'; import { SignalService } from '../../../../protobuf';
import { EncryptionType } from '../../../../session/types/EncryptionType';
import { PubKey, RawMessage } from '../../../../session/types'; import { PubKey, RawMessage } from '../../../../session/types';
import { MessageUtils, UserUtils } from '../../../../session/utils'; import { MessageUtils, UserUtils } from '../../../../session/utils';
import { ApiV2 } from '../../../../session/apis/open_group_api/opengroupV2'; import { ApiV2 } from '../../../../session/apis/open_group_api/opengroupV2';
@ -26,10 +25,10 @@ describe('MessageSender', () => {
describe('send', () => { describe('send', () => {
const ourNumber = '0123456789abcdef'; const ourNumber = '0123456789abcdef';
let sessionMessageAPISendStub: sinon.SinonStub<any>; let sessionMessageAPISendStub: sinon.SinonStub<any>;
let encryptStub: sinon.SinonStub<[PubKey, Uint8Array, EncryptionType]>; let encryptStub: sinon.SinonStub<[PubKey, Uint8Array, SignalService.Envelope.Type]>;
beforeEach(() => { beforeEach(() => {
sessionMessageAPISendStub = Sinon.stub(MessageSender, 'TEST_sendMessageToSnode').resolves(); sessionMessageAPISendStub = Sinon.stub(MessageSender, 'sendMessageToSnode').resolves();
Sinon.stub(Data, 'getMessageById').resolves(); Sinon.stub(Data, 'getMessageById').resolves();

@ -20,6 +20,8 @@ import {
} from '../../../../models/conversation'; } from '../../../../models/conversation';
import { PubKey } from '../../../../session/types'; import { PubKey } from '../../../../session/types';
import { generateFakeSnodes } from '../../../test-utils/utils'; import { generateFakeSnodes } from '../../../test-utils/utils';
import { resetHardForkCachedValues } from '../../../../session/apis/snode_api/hfHandling';
import { sleepFor } from '../../../../session/utils/Promise';
// tslint:disable: chai-vague-errors // tslint:disable: chai-vague-errors
chai.use(chaiAsPromised as any); chai.use(chaiAsPromised as any);
@ -37,6 +39,7 @@ describe('SwarmPolling', () => {
let pollOnceForKeySpy: Sinon.SinonSpy<any>; let pollOnceForKeySpy: Sinon.SinonSpy<any>;
let swarmPolling: SwarmPolling; let swarmPolling: SwarmPolling;
let getItemByIdStub: Sinon.SinonStub;
let clock: Sinon.SinonFakeTimers; let clock: Sinon.SinonFakeTimers;
beforeEach(async () => { beforeEach(async () => {
@ -44,7 +47,7 @@ describe('SwarmPolling', () => {
Sinon.stub(UserUtils, 'getOurPubKeyStrFromCache').returns(ourNumber); Sinon.stub(UserUtils, 'getOurPubKeyStrFromCache').returns(ourNumber);
Sinon.stub(Data, 'getAllConversations').resolves(new ConversationCollection()); Sinon.stub(Data, 'getAllConversations').resolves(new ConversationCollection());
Sinon.stub(DataItem, 'getItemById').resolves(); getItemByIdStub = TestUtils.stubDataItem('getItemById');
Sinon.stub(Data, 'saveConversation').resolves(); Sinon.stub(Data, 'saveConversation').resolves();
Sinon.stub(Data, 'getSwarmNodesForPubkey').resolves(); Sinon.stub(Data, 'getSwarmNodesForPubkey').resolves();
Sinon.stub(Data, 'getLastHashBySnode').resolves(); Sinon.stub(Data, 'getLastHashBySnode').resolves();
@ -70,6 +73,7 @@ describe('SwarmPolling', () => {
Sinon.restore(); Sinon.restore();
getConversationController().reset(); getConversationController().reset();
clock.restore(); clock.restore();
resetHardForkCachedValues();
}); });
describe('getPollingTimeout', () => { describe('getPollingTimeout', () => {
@ -130,6 +134,12 @@ describe('SwarmPolling', () => {
}); });
describe('pollForAllKeys', () => { describe('pollForAllKeys', () => {
beforeEach(() => {
Sinon.stub(DataItem, 'createOrUpdateItem').resolves();
});
afterEach(() => {
Sinon.restore();
});
it('does run for our pubkey even if activeAt is really old ', async () => { it('does run for our pubkey even if activeAt is really old ', async () => {
const convo = getConversationController().getOrCreate( const convo = getConversationController().getOrCreate(
ourNumber, ourNumber,
@ -139,7 +149,19 @@ 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]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]);
});
it('does run for our pubkey even if activeAt is recent ', async () => {
const convo = getConversationController().getOrCreate(
ourNumber,
ConversationTypeEnum.PRIVATE
);
convo.set('active_at', Date.now());
await swarmPolling.start(true);
expect(pollOnceForKeySpy.callCount).to.eq(1);
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 () => {
@ -151,7 +173,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]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]);
}); });
it('does run for group pubkey on start no matter the recent timestamp ', async () => { it('does run for group pubkey on start no matter the recent timestamp ', async () => {
@ -166,11 +188,11 @@ 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]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, 0]);
}); });
it('does run for group pubkey on start no matter the old timestamp ', async () => { it('does run for group pubkey on start no matter the old timestamp if HF < 19', async () => {
const convo = getConversationController().getOrCreate( const convo = getConversationController().getOrCreate(
TestUtils.generateFakePubKeyStr(), TestUtils.generateFakePubKeyStr(),
ConversationTypeEnum.GROUP ConversationTypeEnum.GROUP
@ -183,8 +205,67 @@ 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]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, 0]);
});
it('does run for group pubkey on start no matter the old timestamp if HF >= 19.0 & < 19.1 ', async () => {
const convo = getConversationController().getOrCreate(
TestUtils.generateFakePubKeyStr(),
ConversationTypeEnum.GROUP
);
getItemByIdStub.restore();
getItemByIdStub = TestUtils.stubDataItem('getItemById');
getItemByIdStub
.withArgs('hasSeenHardfork190')
.resolves({ id: 'hasSeenHardfork190', value: true })
.withArgs('hasSeenHardfork191')
.resolves({ id: 'hasSeenHardfork191', value: false });
convo.set('active_at', 1);
const groupConvoPubkey = PubKey.cast(convo.id as string);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
// our pubkey will be polled for, hence the 2
expect(pollOnceForKeySpy.callCount).to.eq(3);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, 0]);
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([groupConvoPubkey, true, -10]);
getItemByIdStub.restore();
getItemByIdStub = TestUtils.stubDataItem('getItemById');
getItemByIdStub.resolves();
});
it('does only poll from -10 for closed groups if HF >= 19.1 ', async () => {
const convo = getConversationController().getOrCreate(
TestUtils.generateFakePubKeyStr(),
ConversationTypeEnum.GROUP
);
getItemByIdStub.restore();
getItemByIdStub = TestUtils.stubDataItem('getItemById');
getItemByIdStub
.withArgs('hasSeenHardfork190')
.resolves({ id: 'hasSeenHardfork190', value: true })
.withArgs('hasSeenHardfork191')
.resolves({ id: 'hasSeenHardfork191', value: true });
convo.set('active_at', 1);
const groupConvoPubkey = PubKey.cast(convo.id as string);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
// our pubkey will be polled for, hence the 2
expect(pollOnceForKeySpy.callCount).to.eq(2);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, -10]);
getItemByIdStub.restore();
getItemByIdStub = TestUtils.stubDataItem('getItemById');
getItemByIdStub.resolves();
}); });
it('does run for group pubkey on start but not another time if activeAt is old ', async () => { it('does run for group pubkey on start but not another time if activeAt is old ', async () => {
@ -204,9 +285,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]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, 0]);
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]); 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 () => {
@ -219,14 +300,21 @@ describe('SwarmPolling', () => {
const groupConvoPubkey = PubKey.cast(convo.id as string); const groupConvoPubkey = PubKey.cast(convo.id as string);
swarmPolling.addGroupId(groupConvoPubkey); swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true); await swarmPolling.start(true);
clock.tick(6000); clock.tick(9000);
// no need to do that as the tick will trigger a call in all cases after 5 secs // no need to do that as the tick will trigger a call in all cases after 5 secs await swarmPolling.pollForAllKeys();
// await swarmPolling.pollForAllKeys(); /** this is not easy to explain, but
* - during the swarmPolling.start, we get two calls to pollOnceForKeySpy (one for our id and one for group od)
* - the clock ticks 9sec, and another call of pollOnceForKeySpy get started, but as we do not await them, this test fails.
* the only fix is to restore the clock and force the a small sleep to let the thing run in bg
*/
clock.restore();
await sleepFor(10);
expect(pollOnceForKeySpy.callCount).to.eq(4); expect(pollOnceForKeySpy.callCount).to.eq(4);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]);
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]); expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, 0]);
expect(pollOnceForKeySpy.lastCall.args).to.deep.eq([groupConvoPubkey, true]); expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, 0]);
expect(pollOnceForKeySpy.getCall(3).args).to.deep.eq([groupConvoPubkey, true, 0]);
}); });
it('does run twice if activeAt is inactive and we tick longer than 2 minutes', async () => { it('does run twice if activeAt is inactive and we tick longer than 2 minutes', async () => {
@ -246,13 +334,19 @@ describe('SwarmPolling', () => {
// more than week old, so inactive group but we have to tick after more than 2 min // more than week old, so inactive group but we have to tick after more than 2 min
convo.set('active_at', Date.now() - 7 * 25 * 3600 * 1000); convo.set('active_at', Date.now() - 7 * 25 * 3600 * 1000);
clock.tick(timeToTick); clock.tick(timeToTick);
/** this is not easy to explain, but
* - during the swarmPolling.start, we get two calls to pollOnceForKeySpy (one for our id and one for group od)
* - the clock ticks 9sec, and another call of pollOnceForKeySpy get started, but as we do not await them, this test fails.
* the only fix is to restore the clock and force the a small sleep to let the thing run in bg
*/
clock.restore();
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]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, 0]);
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]); expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, 0]);
expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true]); expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true, 0]);
}); });
it('does run once only if group is inactive and we tick less than 2 minutes ', async () => { it('does run once only if group is inactive and we tick less than 2 minutes ', async () => {
@ -274,8 +368,8 @@ 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]); // this one comes from the swarmPolling.start expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, 0]); // this one comes from the swarmPolling.start
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]); expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, 0]);
}); });
describe('multiple runs', () => { describe('multiple runs', () => {
@ -309,11 +403,11 @@ 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]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([groupConvoPubkey, true, 0]);
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]); expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([ourPubkey, false, 0]);
expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true]); expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true, 0]);
}); });
it('does run twice if activeAt is more than 2 days old and we tick more than one minute ', async () => { it('does run twice if activeAt is more than 2 days old and we tick more than one minute ', async () => {
@ -329,11 +423,12 @@ 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]); expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([groupConvoPubkey, true, 0]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([ourPubkey, false, 0]);
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]); expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true, 0]);
expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true]);
}); });
}); });
}); });

@ -3,7 +3,7 @@
import chai from 'chai'; import chai from 'chai';
import { TestUtils } from '../../../test-utils'; import { TestUtils } from '../../../test-utils';
import { MessageUtils, UserUtils } from '../../../../session/utils'; import { MessageUtils, UserUtils } from '../../../../session/utils';
import { EncryptionType, PubKey } from '../../../../session/types'; import { PubKey } from '../../../../session/types';
import { ClosedGroupVisibleMessage } from '../../../../session/messages/outgoing/visibleMessage/ClosedGroupVisibleMessage'; import { ClosedGroupVisibleMessage } from '../../../../session/messages/outgoing/visibleMessage/ClosedGroupVisibleMessage';
import { ConfigurationMessage } from '../../../../session/messages/outgoing/controlMessage/ConfigurationMessage'; import { ConfigurationMessage } from '../../../../session/messages/outgoing/controlMessage/ConfigurationMessage';
@ -73,7 +73,7 @@ describe('Message Utils', () => {
const rawMessage = await MessageUtils.toRawMessage(device, message); const rawMessage = await MessageUtils.toRawMessage(device, message);
const derivedPubKey = PubKey.from(rawMessage.device); const derivedPubKey = PubKey.from(rawMessage.device);
expect(derivedPubKey).to.exist; expect(derivedPubKey).to.not.be.eq(undefined, 'should maintain pubkey');
expect(derivedPubKey?.isEqual(device)).to.equal( expect(derivedPubKey?.isEqual(device)).to.equal(
true, true,
'pubkey of message was not converted correctly' 'pubkey of message was not converted correctly'
@ -87,7 +87,7 @@ describe('Message Utils', () => {
const message = new ClosedGroupVisibleMessage({ chatMessage, groupId }); const message = new ClosedGroupVisibleMessage({ chatMessage, groupId });
const rawMessage = await MessageUtils.toRawMessage(device, message); const rawMessage = await MessageUtils.toRawMessage(device, message);
expect(rawMessage.encryption).to.equal(EncryptionType.ClosedGroup); expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE);
}); });
it('should set encryption to Fallback on other messages', async () => { it('should set encryption to Fallback on other messages', async () => {
@ -95,7 +95,7 @@ describe('Message Utils', () => {
const message = TestUtils.generateVisibleMessage(); const message = TestUtils.generateVisibleMessage();
const rawMessage = await MessageUtils.toRawMessage(device, message); const rawMessage = await MessageUtils.toRawMessage(device, message);
expect(rawMessage.encryption).to.equal(EncryptionType.Fallback); expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.SESSION_MESSAGE);
}); });
it('passing ClosedGroupNewMessage returns Fallback', async () => { it('passing ClosedGroupNewMessage returns Fallback', async () => {
@ -112,7 +112,7 @@ describe('Message Utils', () => {
expireTimer: 0, expireTimer: 0,
}); });
const rawMessage = await MessageUtils.toRawMessage(device, msg); const rawMessage = await MessageUtils.toRawMessage(device, msg);
expect(rawMessage.encryption).to.equal(EncryptionType.Fallback); expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.SESSION_MESSAGE);
}); });
it('passing ClosedGroupNameChangeMessage returns ClosedGroup', async () => { it('passing ClosedGroupNameChangeMessage returns ClosedGroup', async () => {
@ -124,7 +124,7 @@ describe('Message Utils', () => {
groupId: TestUtils.generateFakePubKey().key, groupId: TestUtils.generateFakePubKey().key,
}); });
const rawMessage = await MessageUtils.toRawMessage(device, msg); const rawMessage = await MessageUtils.toRawMessage(device, msg);
expect(rawMessage.encryption).to.equal(EncryptionType.ClosedGroup); expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE);
}); });
it('passing ClosedGroupAddedMembersMessage returns ClosedGroup', async () => { it('passing ClosedGroupAddedMembersMessage returns ClosedGroup', async () => {
@ -136,7 +136,7 @@ describe('Message Utils', () => {
groupId: TestUtils.generateFakePubKey().key, groupId: TestUtils.generateFakePubKey().key,
}); });
const rawMessage = await MessageUtils.toRawMessage(device, msg); const rawMessage = await MessageUtils.toRawMessage(device, msg);
expect(rawMessage.encryption).to.equal(EncryptionType.ClosedGroup); expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE);
}); });
it('passing ClosedGroupRemovedMembersMessage returns ClosedGroup', async () => { it('passing ClosedGroupRemovedMembersMessage returns ClosedGroup', async () => {
@ -148,7 +148,7 @@ describe('Message Utils', () => {
groupId: TestUtils.generateFakePubKey().key, groupId: TestUtils.generateFakePubKey().key,
}); });
const rawMessage = await MessageUtils.toRawMessage(device, msg); const rawMessage = await MessageUtils.toRawMessage(device, msg);
expect(rawMessage.encryption).to.equal(EncryptionType.ClosedGroup); expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE);
}); });
it('passing ClosedGroupEncryptionPairMessage returns ClosedGroup', async () => { it('passing ClosedGroupEncryptionPairMessage returns ClosedGroup', async () => {
@ -169,7 +169,7 @@ describe('Message Utils', () => {
encryptedKeyPairs: fakeWrappers, encryptedKeyPairs: fakeWrappers,
}); });
const rawMessage = await MessageUtils.toRawMessage(device, msg); const rawMessage = await MessageUtils.toRawMessage(device, msg);
expect(rawMessage.encryption).to.equal(EncryptionType.ClosedGroup); expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE);
}); });
it('passing ClosedGroupEncryptionKeyPairReply returns Fallback', async () => { it('passing ClosedGroupEncryptionKeyPairReply returns Fallback', async () => {
@ -190,7 +190,7 @@ describe('Message Utils', () => {
encryptedKeyPairs: fakeWrappers, encryptedKeyPairs: fakeWrappers,
}); });
const rawMessage = await MessageUtils.toRawMessage(device, msg); const rawMessage = await MessageUtils.toRawMessage(device, msg);
expect(rawMessage.encryption).to.equal(EncryptionType.Fallback); expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.SESSION_MESSAGE);
}); });
it('passing a ConfigurationMessage returns Fallback', async () => { it('passing a ConfigurationMessage returns Fallback', async () => {
@ -204,7 +204,7 @@ describe('Message Utils', () => {
contacts: [], contacts: [],
}); });
const rawMessage = await MessageUtils.toRawMessage(device, msg); const rawMessage = await MessageUtils.toRawMessage(device, msg);
expect(rawMessage.encryption).to.equal(EncryptionType.Fallback); expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.SESSION_MESSAGE);
}); });
}); });

Loading…
Cancel
Save