added namespace field and signature using it for our own 'retrieve'

pull/2290/head
Audric Ackermann 3 years ago
parent 6e2b2d44f8
commit 50ca1bcda9
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -7,7 +7,7 @@ import {
requiredSnodesForAgreement,
} from './snodePool';
import { getSodiumRenderer } from '../../crypto';
import _, { range } from 'lodash';
import _, { isEmpty, range } from 'lodash';
import pRetry from 'p-retry';
import {
fromBase64ToArray,
@ -21,6 +21,7 @@ import { updateIsOnline } from '../../../state/ducks/onion';
import { ed25519Str } from '../../onions/onionPath';
import { StringUtils, UserUtils } from '../../utils';
import { SnodePool } from '.';
import { handleHardforkResult } from './hfHandling';
// ONS name can have [a-zA-Z0-9_-] except that - is not allowed as start or end
// do not define a regex but rather create it on the fly to avoid https://stackoverflow.com/questions/3891641/regex-test-only-works-every-other-time
@ -454,22 +455,71 @@ export async function storeOnNode(
}
}
async function getRetrieveSignatureParams(
params: RetrieveRequestParams
): Promise<{ timestamp: number; signature: string; pubkey_ed25519: string } | null> {
const ourPubkey = UserUtils.getOurPubKeyFromCache();
const ourEd25519Key = await UserUtils.getUserED25519KeyPair();
if (isEmpty(params?.pubKey) || ourPubkey.key !== params.pubKey || !ourEd25519Key) {
return null;
}
const hasNamespace = params.namespace && params.namespace !== 0;
const namespace = params.namespace || 0;
const edKeyPrivBytes = fromHexToArray(ourEd25519Key?.privKey);
const signatureTimestamp = getNowWithNetworkOffset();
const verificationData = hasNamespace
? StringUtils.encode(`retrieve${namespace}${signatureTimestamp}`, 'utf8')
: StringUtils.encode(`retrieve${signatureTimestamp}`, 'utf8');
const message = new Uint8Array(verificationData);
const sodium = await getSodiumRenderer();
try {
const signature = sodium.crypto_sign_detached(message, edKeyPrivBytes);
const signatureBase64 = fromUInt8ArrayToBase64(signature);
const namespaceObject = hasNamespace ? { namespace } : {};
return {
timestamp: signatureTimestamp,
signature: signatureBase64,
pubkey_ed25519: ourEd25519Key.pubKey,
...namespaceObject,
};
} catch (e) {
window.log.warn('getSignatureParams failed with: ', e.message);
return null;
}
}
type RetrieveRequestParams = {
pubKey: string;
lastHash: string;
namespace?: number;
};
/** */
export async function retrieveNextMessages(
targetNode: Snode,
lastHash: string,
associatedWith: string
associatedWith: string,
namespace?: number
): Promise<Array<any>> {
const params = {
const params: RetrieveRequestParams = {
pubKey: associatedWith,
lastHash: lastHash || '',
namespace: namespace || 0,
};
const signatureParams = (await getRetrieveSignatureParams(params)) || {};
// let exceptions bubble up
// no retry for this one as this a call we do every few seconds while polling for messages
const result = await snodeRpc({
method: 'retrieve',
params,
params: { ...signatureParams, ...params },
targetNode,
associatedWith,
timeout: 4000,
@ -498,6 +548,7 @@ export async function retrieveNextMessages(
}
handleTimestampOffset('retrieve', json.t);
await handleHardforkResult(json);
return json.messages || [];
} catch (e) {
@ -517,7 +568,7 @@ export async function retrieveNextMessages(
* @returns timestamp of the response from snode
*/
// tslint:disable-next-line: variable-name
export const TEST_getNetworkTime = async (snode: Snode): Promise<string | number> => {
export const getNetworkTime = async (snode: Snode): Promise<string | number> => {
const response: any = await snodeRpc({ method: 'info', params: {}, targetNode: snode });
const body = JSON.parse(response.body);
const timestamp = body?.timestamp;
@ -554,7 +605,7 @@ export const forceNetworkDeletion = async (): Promise<Array<string> | null> => {
return pRetry(
async () => {
const timestamp = await exports.TEST_getNetworkTime(snodeToMakeRequestTo);
const timestamp = await exports.getNetworkTime(snodeToMakeRequestTo);
const verificationData = StringUtils.encode(`delete_all${timestamp}`, 'utf8');
const message = new Uint8Array(verificationData);

@ -0,0 +1,92 @@
import { isNumber } from 'lodash';
import { createOrUpdateItem, getItemById } from '../../../data/channelsItem';
let hasSeenHardfork190: boolean | undefined;
let hasSeenHardfork191: boolean | undefined;
/**
* this is only intended for testing. Do not call this in production.
*/
export function resetHardForkCachedValues() {
hasSeenHardfork190 = hasSeenHardfork191 = undefined;
}
export async function getHasSeenHF190() {
if (hasSeenHardfork190 === undefined) {
// read values from db and cache them as it looks like we did not
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.
if (oldHhasSeenHardfork190 === undefined) {
await createOrUpdateItem({ id: 'hasSeenHardfork190', value: false });
hasSeenHardfork190 = false;
} else {
hasSeenHardfork190 = oldHhasSeenHardfork190;
}
}
return hasSeenHardfork190;
}
export async function getHasSeenHF191() {
if (hasSeenHardfork191 === undefined) {
// read values from db and cache them as it looks like we did not
const oldHhasSeenHardfork191 = (await getItemById('hasSeenHardfork191'))?.value;
// values do not exist in the db yet. Let's store false for now in the db and update our cached value.
if (oldHhasSeenHardfork191 === undefined) {
await createOrUpdateItem({ id: 'hasSeenHardfork191', value: false });
hasSeenHardfork191 = false;
} else {
hasSeenHardfork191 = oldHhasSeenHardfork191;
}
}
return hasSeenHardfork191;
}
export async function handleHardforkResult(json: Record<string, any>) {
if (hasSeenHardfork190 === undefined || hasSeenHardfork191 === undefined) {
// read values from db and cache them as it looks like we did not
const oldHhasSeenHardfork190 = (await getItemById('hasSeenHardfork190'))?.value;
const oldHasSeenHardfork191 = (await getItemById('hasSeenHardfork191'))?.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) {
await createOrUpdateItem({ id: 'hasSeenHardfork190', value: false });
hasSeenHardfork190 = false;
} else {
hasSeenHardfork190 = oldHhasSeenHardfork190;
}
if (oldHasSeenHardfork191 === undefined) {
await createOrUpdateItem({ id: 'hasSeenHardfork191', value: false });
hasSeenHardfork191 = false;
} else {
hasSeenHardfork191 = oldHasSeenHardfork191;
}
}
if (hasSeenHardfork191 && hasSeenHardfork190) {
// no need to do any of this if we already know both forks happened
window.log.info('hardfork 19.1 already happened. No need to go any further');
return;
}
// json.hf is an array of 2 number if it is set. Make sure this is the case before doing anything else
if (
json?.hf &&
Array.isArray(json.hf) &&
json.hf.length === 2 &&
isNumber(json.hf[0]) &&
isNumber(json.hf[1])
) {
if (!hasSeenHardfork190 && json.hf[0] >= 19 && json.hf[1] >= 0) {
window.log.info('[HF]: We just detected HF 19.0 on "retrieve"');
await createOrUpdateItem({ id: 'hasSeenHardfork190', value: true });
hasSeenHardfork190 = true;
}
if (!hasSeenHardfork191 && json.hf[0] >= 19 && json.hf[1] >= 1) {
window.log.info('[HF]: We just detected HF 19.1 on "retrieve"');
await createOrUpdateItem({ id: 'hasSeenHardfork191', value: true });
hasSeenHardfork191 = true;
}
}
}

@ -259,7 +259,9 @@ async function processOnionRequestErrorAtDestination({
if (statusCode === 200) {
return;
}
window?.log?.info('processOnionRequestErrorAtDestination. statusCode nok:', statusCode);
window?.log?.info(
`processOnionRequestErrorAtDestination. statusCode nok: ${statusCode}: "${body}"`
);
process406Error(statusCode);
await process421Error(statusCode, body, associatedWith, destinationEd25519);

@ -111,7 +111,7 @@ export async function snodeRpc(
timeout = 10000,
}: {
method: string;
params: any;
params: Record<string, any>;
targetNode: Snode;
associatedWith?: string;
timeout?: number;
@ -128,6 +128,7 @@ export async function snodeRpc(
pubKey: getStoragePubKey(params.pubKey),
};
}
const body = {
jsonrpc: '2.0',
id: '0',

@ -21,15 +21,6 @@ export function decode(buffer: BufferType, stringEncoding: Encoding): string {
return ByteBuffer.wrap(buffer).toString(stringEncoding);
}
/**
* Typescript which can be used to filter out undefined or null values from an array.
* And making typescript realize that there is no nullish value in the type anymore.
* @param v the value to evaluate
*/
export function nonNullish<V>(v: V): v is NonNullable<V> {
return v !== undefined && v !== null;
}
export const toHex = (d: BufferType) => decode(d, 'hex');
export const fromHex = (d: string) => encode(d, 'hex');

@ -66,8 +66,8 @@ export async function getIdentityKeyPair(): Promise<SessionKeyPair | undefined>
export async function getUserED25519KeyPair(): Promise<HexKeyPair | undefined> {
// 'identityKey' keeps the ed25519KeyPair under a ed25519KeyPair field.
// it is only set if the user migrated to the ed25519 way of generating a key
const item = await getItemById('identityKey');
const ed25519KeyPair = item?.value?.ed25519KeyPair;
const item = await getIdentityKeyPair();
const ed25519KeyPair = (item as any)?.ed25519KeyPair;
if (ed25519KeyPair?.publicKey && ed25519KeyPair?.privateKey) {
const pubKeyAsArray = _.map(ed25519KeyPair.publicKey, a => a);
const privKeyAsArray = _.map(ed25519KeyPair.privateKey, a => a);

@ -0,0 +1,347 @@
import { expect } from 'chai';
import Sinon from 'sinon';
import {
getHasSeenHF190,
getHasSeenHF191,
handleHardforkResult,
resetHardForkCachedValues,
} from '../../../../session/apis/snode_api/hfHandling';
import { TestUtils } from '../../../test-utils';
// tslint:disable-next-line: max-func-body-length
describe('hardfork handling', () => {
describe('getHasSeenHF190', () => {
afterEach(() => {
Sinon.restore();
resetHardForkCachedValues();
});
it('fetches from db if undefined, and write to db false if db value is undefined', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves(undefined);
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
const ret = await getHasSeenHF190();
expect(ret).to.be.eq(false, 'getHasSeenHF190 should return false');
expect(getItemById.calledOnce).to.be.eq(true, 'getItemById should have been called');
expect(createItem.calledOnce).to.be.eq(true, 'createItem should have been called');
expect(createItem.args[0][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork190', value: false },
'createItem should have been to set hasSeenHardfork190 to false in the db'
);
});
it('fetches from db if undefined, and does not write to db if db value is not undefined', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves({
id: 'getHasSeenHF190',
value: false,
});
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
const ret = await getHasSeenHF190();
expect(ret).to.be.eq(false, 'getHasSeenHF190 should return false');
expect(getItemById.calledOnce).to.be.eq(true, 'getItemById should have been called');
expect(createItem.notCalled).to.be.eq(true, 'createItem should not have been called');
});
it('fetches from db if undefined, and does not write to db if db value is not undefined - 2', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves({
id: 'getHasSeenHF190',
value: true,
});
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
const ret = await getHasSeenHF190();
expect(ret).to.be.eq(true, 'getHasSeenHF190 should return false');
expect(getItemById.calledOnce).to.be.eq(true, 'getItemById should have been called');
expect(createItem.notCalled).to.be.eq(true, 'createItem should not have been called');
});
it('fetches from db only the value is not cached already', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves({
id: 'getHasSeenHF190',
value: true,
});
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
const ret = await getHasSeenHF190();
const ret2 = await getHasSeenHF190();
expect(ret).to.be.eq(true, 'getHasSeenHF190 should return false');
expect(ret2).to.be.eq(true, 'getHasSeenHF190 should return false - 2');
expect(getItemById.calledOnce).to.be.eq(true, 'getItemById should have been called');
expect(createItem.notCalled).to.be.eq(true, 'createItem should not have been called');
});
});
describe('getHasSeenHF191', () => {
afterEach(() => {
Sinon.restore();
resetHardForkCachedValues();
});
it('fetches from db if undefined, and write to db false if db value is undefined', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves(undefined);
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
const ret = await getHasSeenHF191();
expect(ret).to.be.eq(false, 'getHasSeenHF191 should return false');
expect(getItemById.calledOnce).to.be.eq(true, 'getItemById should have been called');
expect(createItem.calledOnce).to.be.eq(true, 'createItem should have been called');
expect(createItem.args[0][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork191', value: false },
'createItem should have been to set hasSeenHardfork191 to false in the db'
);
});
it('fetches from db if undefined, and does not write to db if db value is not undefined', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves({
id: 'getHasSeenHF191',
value: false,
});
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
const ret = await getHasSeenHF191();
expect(ret).to.be.eq(false, 'getHasSeenHF191 should return false');
expect(getItemById.calledOnce).to.be.eq(true, 'getItemById should have been called');
expect(createItem.notCalled).to.be.eq(true, 'createItem should not have been called');
});
it('fetches from db if undefined, and does not write to db if db value is not undefined - 2', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves({
id: 'getHasSeenHF191',
value: true,
});
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
const ret = await getHasSeenHF191();
expect(ret).to.be.eq(true, 'getHasSeenHF191 should return false');
expect(getItemById.calledOnce).to.be.eq(true, 'getItemById should have been called');
expect(createItem.notCalled).to.be.eq(true, 'createItem should not have been called');
});
it('fetches from db only the value is not cached already', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves({
id: 'getHasSeenHF191',
value: true,
});
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
const ret = await getHasSeenHF191();
const ret2 = await getHasSeenHF191();
expect(ret).to.be.eq(true, 'getHasSeenHF191 should return false');
expect(ret2).to.be.eq(true, 'getHasSeenHF191 should return false - 2');
expect(getItemById.calledOnce).to.be.eq(true, 'getItemById should have been called');
expect(createItem.notCalled).to.be.eq(true, 'createItem should not have been called');
});
});
// tslint:disable-next-line: max-func-body-length
describe('handleHardforkResult', () => {
afterEach(() => {
Sinon.restore();
resetHardForkCachedValues();
});
it('does not fail if null is given as json', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves(undefined);
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
await handleHardforkResult(null as any);
expect(getItemById.calledTwice).to.be.eq(true, 'getItemById should have been calledTwice');
expect(createItem.calledTwice).to.be.eq(true, 'createItem should have been calledTwice');
expect(createItem.args[0][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork190', value: false },
'createItem should have been to set hasSeenHardfork190 to false in the db'
);
expect(createItem.args[1][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork191', value: false },
'createItem should have been to set hasSeenHardfork191 to false in the db'
);
});
it('does not fail on empty json object', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves(undefined);
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
await handleHardforkResult({});
expect(getItemById.calledTwice).to.be.eq(true, 'getItemById should have been calledTwice');
expect(createItem.calledTwice).to.be.eq(true, 'createItem should have been calledTwice');
expect(createItem.args[0][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork190', value: false },
'createItem should have been to set hasSeenHardfork190 to false in the db'
);
expect(createItem.args[1][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork191', value: false },
'createItem should have been to set hasSeenHardfork191 to false in the db'
);
});
it('does not fail with invalid array length of 3', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves(undefined);
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
await handleHardforkResult({ hf: [1, 2, 3] });
expect(getItemById.calledTwice).to.be.eq(true, 'getItemById should have been calledTwice');
expect(createItem.calledTwice).to.be.eq(true, 'createItem should have been calledTwice');
expect(createItem.args[0][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork190', value: false },
'createItem should have been to set hasSeenHardfork190 to false in the db'
);
expect(createItem.args[1][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork191', value: false },
'createItem should have been to set hasSeenHardfork191 to false in the db'
);
});
it('does not fail with invalid array length of 3', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves(undefined);
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
await handleHardforkResult({ hf: [1, 2, 3] });
expect(getItemById.calledTwice).to.be.eq(true, 'getItemById should have been calledTwice');
expect(createItem.calledTwice).to.be.eq(true, 'createItem should have been calledTwice');
expect(createItem.args[0][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork190', value: false },
'createItem should have been to set hasSeenHardfork190 to false in the db'
);
expect(createItem.args[1][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork191', value: false },
'createItem should have been to set hasSeenHardfork191 to false in the db'
);
});
it('does not fail with invalid array length of but not numbers', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves(undefined);
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
await handleHardforkResult({ hf: ['1', 2] });
expect(getItemById.calledTwice).to.be.eq(true, 'getItemById should have been calledTwice');
expect(createItem.calledTwice).to.be.eq(true, 'createItem should have been calledTwice');
expect(createItem.args[0][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork190', value: false },
'createItem should have been to set hasSeenHardfork190 to false in the db'
);
expect(createItem.args[1][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork191', value: false },
'createItem should have been to set hasSeenHardfork191 to false in the db'
);
});
it('does not fail with invalid array length of 1 ', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves(undefined);
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
await handleHardforkResult({ hf: [1] });
expect(getItemById.calledTwice).to.be.eq(true, 'getItemById should have been calledTwice');
expect(createItem.calledTwice).to.be.eq(true, 'createItem should have been calledTwice');
expect(createItem.args[0][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork190', value: false },
'createItem should have been to set hasSeenHardfork190 to false in the db'
);
expect(createItem.args[1][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork191', value: false },
'createItem should have been to set hasSeenHardfork191 to false in the db'
);
});
it('does not write new data if hf major is <= 18 ', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves(undefined);
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
await handleHardforkResult({ hf: [18, 9] });
expect(getItemById.calledTwice).to.be.eq(true, 'getItemById should have been calledTwice');
expect(createItem.calledTwice).to.be.eq(true, 'createItem should have been calledTwice');
expect(createItem.args[0][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork190', value: false },
'createItem should have been to set hasSeenHardfork190 to false in the db'
);
expect(createItem.args[1][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork191', value: false },
'createItem should have been to set hasSeenHardfork191 to false in the db'
);
});
it('does write new data if hf major is === 19 and minor === 0 ', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves(undefined);
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
await handleHardforkResult({ hf: [19, 0] });
expect(getItemById.calledTwice).to.be.eq(true, 'getItemById should have been calledTwice');
expect(createItem.calledThrice).to.be.eq(true, 'createItem should have been calledThrice');
expect(createItem.args[0][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork190', value: false },
'createItem should have been to set hasSeenHardfork190 to false in the db'
);
expect(createItem.args[1][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork191', value: false },
'createItem should have been to set hasSeenHardfork191 to false in the db'
);
expect(createItem.args[2][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork190', value: true },
'createItem should have been to set hasSeenHardfork191 to true in the db'
);
getItemById.resetHistory();
createItem.resetHistory();
expect(await getHasSeenHF190()).to.be.eq(true, 'getHasSeenHF190 should have been true');
expect(getItemById.notCalled).to.be.eq(true, 'getItemById should not have been called more');
expect(createItem.notCalled).to.be.eq(true, 'createItem should not have been called more');
});
it('does write new data if hf major is === 19 and minor === 1 ', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves(undefined);
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
await handleHardforkResult({ hf: [19, 1] });
expect(getItemById.calledTwice).to.be.eq(true, 'getItemById should have been calledTwice');
expect(createItem.callCount).to.be.eq(4, 'createItem should have been 4');
expect(createItem.args[0][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork190', value: false },
'createItem should have been to set hasSeenHardfork190 to false in the db'
);
expect(createItem.args[1][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork191', value: false },
'createItem should have been to set hasSeenHardfork191 to false in the db'
);
expect(createItem.args[2][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork190', value: true },
'createItem should have been to set hasSeenHardfork190 to true in the db'
);
expect(createItem.args[3][0]).to.be.deep.eq(
{ id: 'hasSeenHardfork191', value: true },
'createItem should have been to set hasSeenHardfork191 to true in the db'
);
getItemById.resetHistory();
createItem.resetHistory();
expect(await getHasSeenHF190()).to.be.eq(true, 'getHasSeenHF190 should have been true');
expect(await getHasSeenHF191()).to.be.eq(true, 'getHasSeenHF191 should have been true');
expect(getItemById.notCalled).to.be.eq(true, 'getItemById should not have been called more');
expect(createItem.notCalled).to.be.eq(true, 'createItem should not have been called more');
});
it('does not write new data if hf major is === 19 and minor === 1 but it is already known we have seen both forks ', async () => {
const getItemById = TestUtils.stubDataItem('getItemById').resolves({ id: '', value: true });
const createItem = TestUtils.stubDataItem('createOrUpdateItem').resolves();
await handleHardforkResult({ hf: [19, 1] });
expect(getItemById.calledTwice).to.be.eq(true, 'getItemById should have been calledTwice');
expect(createItem.callCount).to.be.eq(0, 'createItem should have been 0');
getItemById.resetHistory();
createItem.resetHistory();
expect(await getHasSeenHF190()).to.be.eq(true, 'getHasSeenHF190 should have been true');
expect(await getHasSeenHF191()).to.be.eq(true, 'getHasSeenHF191 should have been true');
expect(getItemById.notCalled).to.be.eq(true, 'getItemById should not have been called more');
expect(createItem.notCalled).to.be.eq(true, 'createItem should not have been called more');
});
});
});
Loading…
Cancel
Save