Skip to content
This repository has been archived by the owner on Jul 15, 2022. It is now read-only.

LL-4373 Discard existing ops on sync if blacklist has changed #1010

Merged
merged 6 commits into from
Jan 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions src/__tests__/__snapshots__/all.libcore.js.snap

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/__tests__/currencies.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ test("sort by marketcap", () => {
"bitcoin",
"ethereum",
"ethereum/erc20/0x_project",
"stealthcoin",
"ethereum/erc20/holotoken",
"stealthcoin",
"ethereum/erc20/hydro_protocol",
"ethereum/erc20/xensor",
]);
Expand Down
2 changes: 0 additions & 2 deletions src/__tests__/test-helpers/libcore-setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import "./setup";
import { setEnvUnsafe } from "../../env";
import implementLibcore from "../../libcore/platforms/nodejs";

jest.setTimeout(180000);

let setupCalled = null;
export const setup = (testId) => {
if (setupCalled) {
Expand Down
2 changes: 0 additions & 2 deletions src/__tests__/test-helpers/libcore-setup.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { listen } from "@ledgerhq/logs";
import "./setup";
import "./implement-react-native-libcore";

jest.setTimeout(180000);

export const setup = (testName) => {
global._JEST_SETUP(testName);
};
Expand Down
2 changes: 2 additions & 0 deletions src/__tests__/test-helpers/setup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { setSupportedCurrencies } from "../../currencies";

jest.setTimeout(180000);

setSupportedCurrencies([
"bitcoin",
"ethereum",
Expand Down
4 changes: 4 additions & 0 deletions src/account/serialization.js
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@ export function fromAccountRaw(rawAccount: AccountRaw): Account {
bitcoinResources,
swapHistory,
algorandResources,
blacklistedTokensCache,
polkadotResources,
} = rawAccount;

Expand Down Expand Up @@ -632,6 +633,7 @@ export function fromAccountRaw(rawAccount: AccountRaw): Account {
currency,
lastSyncDate: new Date(lastSyncDate || 0),
swapHistory: [],
blacklistedTokensCache,
};

if (xpub) {
Expand Down Expand Up @@ -701,6 +703,7 @@ export function toAccountRaw({
bitcoinResources,
swapHistory,
algorandResources,
blacklistedTokensCache,
polkadotResources,
}: Account): AccountRaw {
const res: $Exact<AccountRaw> = {
Expand All @@ -714,6 +717,7 @@ export function toAccountRaw({
freshAddressPath,
freshAddresses,
blockHeight,
blacklistedTokensCache,
creationDate: creationDate.toISOString(),
operationsCount,
operations: (operations || []).map((o) => toOperationRaw(o)),
Expand Down
3 changes: 1 addition & 2 deletions src/families/ethereum/modules/compound.resilience.test.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
// @flow
import "../../../__tests__/test-helpers/setup";
import { reduce } from "rxjs/operators";
import { setSupportedCurrencies } from "../../../currencies";
import { fromAccountRaw, toAccountRaw } from "../../../account";
import type { Account } from "../../../types";
import { getAccountBridge } from "../../../bridge";
import { makeBridgeCacheSystem } from "../../../bridge/cache";
import { ethereum1 } from "../test-dataset";
import { setEnv } from "../../../env";

setSupportedCurrencies(["ethereum"]);
setEnv("COMPOUND_API", "https://status.ledger.com"); // hack to hit an endpoint that actually is going to 404

test("if API is down, an account still sync fine", async () => {
Expand Down
9 changes: 8 additions & 1 deletion src/families/ethereum/synchronisation.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,16 @@ export const getAccountShape: GetAccountShape = async (

// fetch transactions, incrementally if possible
const mostRecentStableOperation = initialStableOperations[0];

const newBlacklistedTokensCache = JSON.stringify(blacklistedTokenIds || []);
const outdatedBlacklist =
initialAccount?.blacklistedTokensCache !== newBlacklistedTokensCache;

let pullFromBlockHash =
initialAccount &&
areAllOperationsLoaded(initialAccount) &&
mostRecentStableOperation
mostRecentStableOperation &&
!outdatedBlacklist
? mostRecentStableOperation.blockHash
: undefined;

Expand Down Expand Up @@ -159,6 +165,7 @@ export const getAccountShape: GetAccountShape = async (
blockHeight,
lastSyncDate: new Date(),
balanceHistory: undefined,
blacklistedTokensCache: newBlacklistedTokensCache,
};

return accountShape;
Expand Down
95 changes: 95 additions & 0 deletions src/families/ethereum/synchronisation.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// @flow
import "../../__tests__/test-helpers/setup";
import { reduce } from "rxjs/operators";
import { fromAccountRaw } from "../../account";
import { getAccountCurrency } from "../../account/helpers";
import type { Account } from "../../types";
import { getAccountBridge } from "../../bridge";
import { makeBridgeCacheSystem } from "../../bridge/cache";
import { ethereum1 } from "./test-dataset";

describe("blacklistedTokenIds functionality", () => {
const account = fromAccountRaw(ethereum1);
let localCache = {};
const cache = makeBridgeCacheSystem({
saveData(c, d) {
localCache[c.id] = d;
return Promise.resolve();
},
getData(c) {
return Promise.resolve(localCache[c.id]);
},
});

test("initial raw account contains no token accounts", async () => {
await cache.prepareCurrency(account.currency);
expect(ethereum1.subAccounts?.length).toBeFalsy();
});

test("sync finds tokens, but not blacklisted ones", async () => {
const bridge = getAccountBridge(account);
const blacklistedTokenIds = ["ethereum/erc20/weth"];
const synced = await bridge
.sync(account, {
paginationConfig: {},
blacklistedTokenIds,
})
.pipe(reduce((a, f: (Account) => Account) => f(a), account))
.toPromise();

// Contains token accounts
expect(synced.subAccounts?.length).toBeTruthy();

// Contains a known token
expect(
synced.subAccounts.find(
(a) => getAccountCurrency(a)?.id === "ethereum/erc20/0x_project"
)
).toBeTruthy();

// Does not contain a blacklisted token
expect(
synced.subAccounts.find((a) =>
blacklistedTokenIds.includes(getAccountCurrency(a)?.id)
)
).toBe(undefined);
});

test("account resyncs tokens if no longer blacklisted", async () => {
const bridge = getAccountBridge(account);
const blacklistedTokenIds = ["ethereum/erc20/weth"];
const syncedWithoutWeth = await bridge
.sync(account, {
paginationConfig: {},
blacklistedTokenIds,
})
.pipe(reduce((a, f: (Account) => Account) => f(a), account))
.toPromise();

// Contains token accounts
expect(syncedWithoutWeth.subAccounts?.length).toBeTruthy();

// Does not contain a blacklisted token
expect(
syncedWithoutWeth.subAccounts.find((a) =>
blacklistedTokenIds.includes(getAccountCurrency(a)?.id)
)
).toBe(undefined);

//Sync again with `syncedWithoutWeth` as a base but without it being blacklisted
const synced = await bridge
.sync(account, {
paginationConfig: {},
blacklistedTokenIds: ["ethereum/erc20/somethingElse"],
})
.pipe(reduce((a, f: (Account) => Account) => f(a), account))
.toPromise();

// Does not contain a blacklisted token
expect(
synced.subAccounts.find(
(a) => getAccountCurrency(a)?.id === "ethereum/erc20/weth"
)
).toBeTruthy();
});
});
5 changes: 5 additions & 0 deletions src/reconciliation.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,11 @@ export function patchAccount(
changed = true;
}

if (account.blacklistedTokensCache !== updatedRaw.blacklistedTokensCache) {
next.blacklistedTokensCache = updatedRaw.blacklistedTokensCache;
changed = true;
}

if (
updatedRaw.tronResources &&
account.tronResources !== updatedRaw.tronResources
Expand Down
4 changes: 4 additions & 0 deletions src/types/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ export type Account = {

// Swap operations linked to this account
swapHistory: SwapOperation[],

// Hash used to discard tx history on sync if blacklisted token ids change
blacklistedTokensCache?: string,
};

export type SubAccount = TokenAccount | ChildAccount;
Expand Down Expand Up @@ -279,6 +282,7 @@ export type AccountRaw = {
polkadotResources?: PolkadotResourcesRaw,
// Swap operations linked to this account
swapHistory?: SwapOperationRaw[],
blacklistedTokensCache?: string,
};

export type SubAccountRaw = TokenAccountRaw | ChildAccountRaw;
Expand Down