diff --git a/packages/mobile/src/networkInfo/saga.ts b/packages/mobile/src/networkInfo/saga.ts index 729a7e9f298..d5f2c1e0e8d 100644 --- a/packages/mobile/src/networkInfo/saga.ts +++ b/packages/mobile/src/networkInfo/saga.ts @@ -6,7 +6,7 @@ import { call, cancelled, put, spawn, take } from 'redux-saga/effects' import { waitForGethConnectivity } from 'src/geth/saga' import { setNetworkConnectivity } from 'src/networkInfo/actions' import Logger from 'src/utils/Logger' -import { checkWeb3Sync } from 'src/web3/saga' +import { waitForWeb3Sync } from 'src/web3/saga' const TAG = 'networkInfo/saga' @@ -17,7 +17,7 @@ export function* waitForRehydrate() { export function* waitWeb3LastBlock() { yield waitForGethConnectivity() - yield checkWeb3Sync() + yield waitForWeb3Sync() } function createNetworkStatusChannel() { diff --git a/packages/mobile/src/web3/actions.ts b/packages/mobile/src/web3/actions.ts index 1badcd4186c..f35ffca0bca 100644 --- a/packages/mobile/src/web3/actions.ts +++ b/packages/mobile/src/web3/actions.ts @@ -32,13 +32,6 @@ export interface SetLatestBlockNumberAction { latestBlockNumber: number } -export interface SetProgressAction { - type: Actions.SET_PROGRESS - payload: { - syncProgress: number - } -} - export interface UpdateWeb3SyncProgressAction { type: Actions.UPDATE_WEB3_SYNC_PROGRESS payload: { @@ -51,7 +44,6 @@ export interface UpdateWeb3SyncProgressAction { export type ActionTypes = | SetAccountAction | SetCommentKeyAction - | SetProgressAction | SetLatestBlockNumberAction | UpdateWeb3SyncProgressAction @@ -75,13 +67,6 @@ export const setLatestBlockNumber = (latestBlockNumber: number): SetLatestBlockN latestBlockNumber, }) -export const setSyncProgress = (syncProgress: number) => ({ - type: Actions.SET_PROGRESS, - payload: { - syncProgress, - }, -}) - // TODO: Remove duplicaiton with SetProgress action (this is currently unused) export const updateWeb3SyncProgress = (payload: { startingBlock: number diff --git a/packages/mobile/src/web3/reducer.ts b/packages/mobile/src/web3/reducer.ts index 6ad5c0b2505..7c10b037aba 100644 --- a/packages/mobile/src/web3/reducer.ts +++ b/packages/mobile/src/web3/reducer.ts @@ -1,11 +1,10 @@ import { getRehydratePayload, REHYDRATE, RehydrateAction } from 'src/redux/persist-helper' -import { Actions, ActionTypes, UpdateWeb3SyncProgressAction } from 'src/web3/actions' +import { Actions, ActionTypes } from 'src/web3/actions' export interface State { - syncProgress: number - syncProgressData: { + syncProgress: { + startingBlock: number currentBlock: number - startBlock: number highestBlock: number } latestBlockNumber: number @@ -14,26 +13,16 @@ export interface State { } const initialState: State = { - syncProgress: 0, - syncProgressData: { + syncProgress: { + startingBlock: 0, currentBlock: 0, highestBlock: 0, - startBlock: 0, }, latestBlockNumber: 0, account: null, commentKey: null, } -function calculateSyncProgress(action: UpdateWeb3SyncProgressAction) { - if (action.payload.currentBlock === 0) { - return 0 - } - const numerator = action.payload.currentBlock - action.payload.startingBlock - const denominator = action.payload.highestBlock - action.payload.startingBlock - return (100 * numerator) / denominator -} - export const reducer = ( state: State | undefined = initialState, action: ActionTypes | RehydrateAction @@ -44,11 +33,10 @@ export const reducer = ( return { ...state, ...getRehydratePayload(action, 'web3'), - syncProgress: 0, - syncProgressData: { + syncProgress: { + startingBlock: 0, currentBlock: 0, highestBlock: 0, - startBlock: 0, }, latestBlockNumber: 0, } @@ -63,11 +51,6 @@ export const reducer = ( ...state, commentKey: action.commentKey, } - case Actions.SET_PROGRESS: - return { - ...state, - syncProgress: action.payload.syncProgress, - } case Actions.SET_BLOCK_NUMBER: return { ...state, @@ -76,7 +59,7 @@ export const reducer = ( case Actions.UPDATE_WEB3_SYNC_PROGRESS: return { ...state, - syncProgress: calculateSyncProgress(action), + syncProgress: action.payload, } default: diff --git a/packages/mobile/src/web3/saga.test.ts b/packages/mobile/src/web3/saga.test.ts index 060494cedf2..c069486d024 100644 --- a/packages/mobile/src/web3/saga.test.ts +++ b/packages/mobile/src/web3/saga.test.ts @@ -1,19 +1,13 @@ import { expectSaga } from 'redux-saga-test-plan' import { call, delay, select } from 'redux-saga/effects' import { pincodeSelector } from 'src/account/reducer' -import { waitForGethConnectivity } from 'src/geth/saga' import { navigateToError } from 'src/navigator/NavigationService' +import { setLatestBlockNumber, updateWeb3SyncProgress } from 'src/web3/actions' import { - getLatestBlock, - setLatestBlockNumber, - setSyncProgress, - updateWeb3SyncProgress, -} from 'src/web3/actions' -import { - _CHECK_SYNC_PROGRESS_TIMEOUT, - _checkWeb3SyncProgressClaim, - checkWeb3Sync, + checkWeb3SyncProgress, createNewAccount, + SYNC_TIMEOUT, + waitForWeb3Sync, } from 'src/web3/saga' import { currentAccountSelector } from 'src/web3/selectors' import { createMockStore, sleep } from 'test/utils' @@ -37,6 +31,7 @@ jest.mock('src/web3/contracts', () => ({ .fn() .mockReturnValueOnce({ startingBlock: 0, currentBlock: 10, highestBlock: 100 }) .mockReturnValueOnce(false), + getBlock: jest.fn(() => ({ number: LAST_BLOCK_NUMBER })), }, }, })) @@ -66,40 +61,37 @@ describe(createNewAccount, () => { }) }) -describe(checkWeb3Sync, () => { +describe(waitForWeb3Sync, () => { it('reports connection successfully', async () => { - await expectSaga(checkWeb3Sync) + await expectSaga(waitForWeb3Sync) .withState(state) .provide([ - [call(waitForGethConnectivity), true], // needs this async function to win the race with a delay - [call(_checkWeb3SyncProgressClaim), call(async () => true)], - [call(getLatestBlock), { number: LAST_BLOCK_NUMBER }], + [call(checkWeb3SyncProgress), call(async () => true)], ]) - .put(setLatestBlockNumber(LAST_BLOCK_NUMBER)) + .returns(true) .run() }) it('times out', async () => { - await expectSaga(checkWeb3Sync) + await expectSaga(waitForWeb3Sync) .withState(state) .provide([ - [call(waitForGethConnectivity), true], - [call(_checkWeb3SyncProgressClaim), call(sleep, 100)], // sleep so timeout always wins the race - [delay(_CHECK_SYNC_PROGRESS_TIMEOUT), true], - [call(getLatestBlock), { number: LAST_BLOCK_NUMBER }], + [call(checkWeb3SyncProgress), call(sleep, 100)], // sleep so timeout always wins the race + [delay(SYNC_TIMEOUT), true], ]) + .returns(false) .run() expect(navigateToError).toHaveBeenCalled() }) }) -describe(_checkWeb3SyncProgressClaim, () => { +describe(checkWeb3SyncProgress, () => { it('reports web3 status correctly', async () => { - await expectSaga(_checkWeb3SyncProgressClaim) + await expectSaga(checkWeb3SyncProgress) .withState(state) .put(updateWeb3SyncProgress({ startingBlock: 0, currentBlock: 10, highestBlock: 100 })) // is syncing the first time - .put(setSyncProgress(100)) // finished syncing the second time + .put(setLatestBlockNumber(LAST_BLOCK_NUMBER)) // finished syncing the second time .returns(true) .run() }) diff --git a/packages/mobile/src/web3/saga.ts b/packages/mobile/src/web3/saga.ts index 70e5e5ea25c..34086361f46 100644 --- a/packages/mobile/src/web3/saga.ts +++ b/packages/mobile/src/web3/saga.ts @@ -13,7 +13,6 @@ import { currentLanguageSelector } from 'src/app/reducers' import { getWordlist } from 'src/backup/utils' import { UNLOCK_DURATION } from 'src/geth/consts' import { deleteChainData } from 'src/geth/geth' -import { waitForGethConnectivity } from 'src/geth/saga' import { navigateToError } from 'src/navigator/NavigationService' import { waitWeb3LastBlock } from 'src/networkInfo/saga' import Logger from 'src/utils/Logger' @@ -23,7 +22,6 @@ import { setAccount, setLatestBlockNumber, setPrivateCommentKey, - setSyncProgress, unlockAccount, updateWeb3SyncProgress, } from 'src/web3/actions' @@ -36,23 +34,34 @@ const MNEMONIC_BIT_LENGTH = (ETH_PRIVATE_KEY_LENGTH * 8) / 2 const TAG = 'web3/saga' // The timeout for web3 to complete syncing and the latestBlock to be > 0 -const CHECK_SYNC_PROGRESS_TIMEOUT = 60000 +export const SYNC_TIMEOUT = 60000 const BLOCK_CHAIN_CORRUPTION_ERROR = "Error: CONNECTION ERROR: Couldn't connect to node on IPC." -// checks if web3 claims it is currently syncing or not -function* checkWeb3SyncProgressClaim() { +// checks if web3 claims it is currently syncing and attempts to wait for it to complete +export function* checkWeb3SyncProgress() { while (true) { try { - const syncProgress = yield web3.eth.isSyncing() // returns true when it's still syncing and thus not ready - if (typeof syncProgress === 'boolean' && !syncProgress) { - Logger.debug(TAG, 'checkWeb3SyncProgressClaim', 'sync complete') + Logger.debug(TAG, 'checkWeb3SyncProgress', 'Checking sync progress') + + // isSyncing returns a syncProgress object when it's still syncing, false otherwise + const syncProgress = yield web3.eth.isSyncing() - yield put(setSyncProgress(100)) - return true + if (typeof syncProgress === 'boolean' && !syncProgress) { + Logger.debug(TAG, 'checkWeb3SyncProgress', 'Sync maybe complete, checking') + + const latestBlock: Block = yield call(getLatestBlock) + if (latestBlock && latestBlock.number > 0) { + yield put(setLatestBlockNumber(latestBlock.number)) + Logger.debug(TAG, 'checkWeb3SyncProgress', 'Sync is complete') + return true + } else { + Logger.debug(TAG, 'checkWeb3SyncProgress', 'Sync not actually complete, still waiting') + } + } else { + yield put(updateWeb3SyncProgress(syncProgress)) } - Logger.debug(TAG, 'checkWeb3SyncProgressClaim', 'sync in progress') - yield put(updateWeb3SyncProgress(syncProgress)) - // not ready yet, keep looping + + yield delay(100) // wait 100ms while web3 syncs } catch (error) { if (error.toString().toLowerCase() === BLOCK_CHAIN_CORRUPTION_ERROR.toLowerCase()) { CeloAnalytics.track(CustomEventNames.blockChainCorruption, {}, true) @@ -60,44 +69,32 @@ function* checkWeb3SyncProgressClaim() { if (deleted) { navigateToError('corruptedChainDeleted') } - throw new Error('Corrupted chain data encountered') } else { - Logger.error(TAG, `checking web3 sync progress: ${error}`) + Logger.error(TAG, 'Unexpected sync error', error) } + return false } } } -// The worker listening to sync progress requests -export function* checkWeb3Sync() { +export function* waitForWeb3Sync() { try { - yield call(waitForGethConnectivity) - try { - const { timeout } = yield race({ - checkProgress: call(checkWeb3SyncProgressClaim), - timeout: delay(CHECK_SYNC_PROGRESS_TIMEOUT), - }) - - if (timeout) { - Logger.error(TAG, 'Could not complete sync progress check') - navigateToError('web3FailedToSync') - } - - const latestBlock: Block = yield call(getLatestBlock) - if (latestBlock && latestBlock.number > 0) { - yield put(setLatestBlockNumber(latestBlock.number)) - } else { - Logger.error( - TAG, - `web3 indicated sync complete, yet the latest block is ${JSON.stringify(latestBlock)}` - ) - } - } catch (error) { - Logger.error(TAG, 'checkWeb3Sync', error) - navigateToError('errorDuringSync') + const { syncComplete, timeout } = yield race({ + syncComplete: call(checkWeb3SyncProgress), + timeout: delay(SYNC_TIMEOUT), + }) + + if (timeout || !syncComplete) { + Logger.error(TAG, 'Could not complete sync') + navigateToError('web3FailedToSync') + return false } + + return true } catch (error) { - Logger.error(TAG, 'checkWeb3Sync saga error', error) + Logger.error(TAG, 'checkWeb3Sync', error) + navigateToError('errorDuringSync') + return false } } @@ -218,7 +215,3 @@ export function* getConnectedUnlockedAccount() { throw new Error(ErrorMessages.INCORRECT_PIN) } } - -// exported for testing -export const _checkWeb3SyncProgressClaim = checkWeb3SyncProgressClaim -export const _CHECK_SYNC_PROGRESS_TIMEOUT = CHECK_SYNC_PROGRESS_TIMEOUT diff --git a/packages/mobile/test/schemas.ts b/packages/mobile/test/schemas.ts index df9ec37f8dd..7ba2735912e 100644 --- a/packages/mobile/test/schemas.ts +++ b/packages/mobile/test/schemas.ts @@ -116,6 +116,14 @@ const v0Schema = { recipients: { recipientCache: {}, }, + web3: { + ...vNeg1Schema.web3, + syncProgress: { + startingBlock: 0, + currentBlock: 0, + highestBlock: 0, + }, + }, } export function getLatestSchema(): Partial {