Unverified Commit a12ebab2 authored by Iain Maitland's avatar Iain Maitland
Browse files

Revert "Revert "reset outgoing auto-vesting-routes on account recovery""

This reverts commit a7dcd5b2.
parent 0b944325
import { takeEvery } from 'redux-saga';
import { call, put, select } from 'redux-saga/effects';
import { call, put, select, takeEvery } from 'redux-saga/effects';
import { Set, Map, fromJS, List } from 'immutable';
import { api } from '@steemit/steem-js';
import { PrivateKey } from '@steemit/steem-js/lib/auth/ecc';
......@@ -14,11 +13,9 @@ const postingOps = Set(
.split(/,\s*/)
);
export const authWatches = [watchForAuth];
function* watchForAuth() {
yield* takeEvery('user/ACCOUNT_AUTH_LOOKUP', accountAuthLookup);
}
export const authWatches = [
takeEvery('user/ACCOUNT_AUTH_LOOKUP', accountAuthLookup),
];
export function* accountAuthLookup({
payload: { account, private_keys, login_owner_pubkey },
......
import { takeLatest, takeEvery } from 'redux-saga';
import { call, put, select, fork } from 'redux-saga/effects';
import {
call,
put,
select,
fork,
takeLatest,
takeEvery,
} from 'redux-saga/effects';
import { loadFollows, fetchFollowCount } from 'app/redux/FollowSaga';
import { getContent } from 'app/redux/SagaShared';
import * as globalActions from './GlobalReducer';
......@@ -13,21 +19,13 @@ const GET_CONTENT = 'fetchDataSaga/GET_CONTENT';
const FETCH_STATE = 'fetchDataSaga/FETCH_STATE';
export const fetchDataWatches = [
watchLocationChange,
watchDataRequests,
watchFetchJsonRequests,
watchFetchState,
watchGetContent,
takeLatest(REQUEST_DATA, fetchData),
takeEvery(GET_CONTENT, getContentCaller),
takeLatest('@@router/LOCATION_CHANGE', fetchState),
takeLatest(FETCH_STATE, fetchState),
takeEvery('global/FETCH_JSON', fetchJson),
];
export function* watchDataRequests() {
yield* takeLatest(REQUEST_DATA, fetchData);
}
export function* watchGetContent() {
yield* takeEvery(GET_CONTENT, getContentCaller);
}
export function* getContentCaller(action) {
yield getContent(action.payload);
}
......@@ -120,14 +118,6 @@ function* getAccounts(usernames) {
yield put(globalActions.receiveAccounts({ accounts }));
}
export function* watchLocationChange() {
yield* takeLatest('@@router/LOCATION_CHANGE', fetchState);
}
export function* watchFetchState() {
yield* takeLatest(FETCH_STATE, fetchState);
}
export function* fetchData(action) {
const { order, author, permlink, accountname } = action.payload;
let { category } = action.payload;
......@@ -367,10 +357,6 @@ export function* fetchMeta({ payload: { id, link } }) {
}
}
export function* watchFetchJsonRequests() {
yield* takeEvery('global/FETCH_JSON', fetchJson);
}
/**
@arg {string} id unique key for result global['fetchJson_' + id]
@arg {string} url
......
import { takeLatest } from 'redux-saga';
import { call, put } from 'redux-saga/effects';
import { call, put, takeLatest } from 'redux-saga/effects';
import { api } from '@steemit/steem-js';
import * as marketActions from './MarketReducer';
......@@ -8,9 +7,9 @@ import * as userActions from './UserReducer';
import { getAccount } from './SagaShared';
export const marketWatches = [
watchLocationChange,
watchUserLogin,
watchMarketUpdate,
takeLatest(userActions.SET_USER, fetchOpenOrders),
takeLatest('@@router/LOCATION_CHANGE', fetchMarket),
takeLatest(marketActions.UPDATE_MARKET, reloadMarket),
];
const wait = ms =>
......@@ -86,15 +85,3 @@ export function* reloadMarket(reload_action) {
yield fetchMarket(reload_action);
yield fetchOpenOrders(reload_action);
}
export function* watchUserLogin() {
yield* takeLatest(userActions.SET_USER, fetchOpenOrders);
}
export function* watchLocationChange() {
yield* takeLatest('@@router/LOCATION_CHANGE', fetchMarket);
}
export function* watchMarketUpdate() {
yield* takeLatest(marketActions.UPDATE_MARKET, reloadMarket);
}
import { fromJS } from 'immutable';
import { call, put, select } from 'redux-saga/effects';
import { takeEvery, takeLatest } from 'redux-saga';
import { call, put, select, takeEvery, takeLatest } from 'redux-saga/effects';
import tt from 'counterpart';
import { api } from '@steemit/steem-js';
import * as globalActions from './GlobalReducer';
......@@ -14,9 +13,16 @@ const wait = ms =>
});
export const sharedWatches = [
watchGetState,
watchTransactionErrors,
watchUserSettingsUpdates,
takeEvery(globalActions.GET_STATE, getState),
takeLatest(
[
appActions.SET_USER_PREFERENCES,
appActions.TOGGLE_NIGHTMODE,
appActions.TOGGLE_BLOGMODE,
],
saveUserPreferences
),
takeEvery('transaction/ERROR', showTransactionErrorNotification),
];
export function* getAccount(username, force = false) {
......@@ -33,9 +39,6 @@ export function* getAccount(username, force = false) {
return account;
}
export function* watchGetState() {
yield* takeEvery(globalActions.GET_STATE, getState);
}
/** Manual refreshes. The router is in FetchDataSaga. */
export function* getState({ payload: { url } }) {
try {
......@@ -47,10 +50,6 @@ export function* getState({ payload: { url } }) {
}
}
export function* watchTransactionErrors() {
yield* takeEvery('transaction/ERROR', showTransactionErrorNotification);
}
function* showTransactionErrorNotification() {
const errors = yield select(state => state.transaction.get('errors'));
for (const [key, message] of errors) {
......@@ -94,14 +93,3 @@ function* saveUserPreferences({ payload }) {
const prefs = yield select(state => state.app.get('user_preferences'));
yield setUserPreferences(prefs.toJS());
}
function* watchUserSettingsUpdates() {
yield* takeLatest(
[
appActions.SET_USER_PREFERENCES,
appActions.TOGGLE_NIGHTMODE,
appActions.TOGGLE_BLOGMODE,
],
saveUserPreferences
);
}
import { takeEvery } from 'redux-saga';
import { call, put, select } from 'redux-saga/effects';
import { call, put, select, all, takeEvery } from 'redux-saga/effects';
import { fromJS, Set, Map } from 'immutable';
import tt from 'counterpart';
import getSlug from 'speakingurl';
......@@ -18,28 +17,12 @@ import { DEBT_TICKER } from 'app/client_config';
import { serverApiRecordEvent } from 'app/utils/ServerApiClient';
export const transactionWatches = [
watchForBroadcast,
watchForUpdateAuthorities,
watchForUpdateMeta,
watchForRecoverAccount,
takeEvery(transactionActions.BROADCAST_OPERATION, broadcastOperation),
takeEvery(transactionActions.UPDATE_AUTHORITIES, updateAuthorities),
takeEvery(transactionActions.UPDATE_META, updateMeta),
takeEvery(transactionActions.RECOVER_ACCOUNT, recoverAccount),
];
export function* watchForBroadcast() {
yield* takeEvery(
transactionActions.BROADCAST_OPERATION,
broadcastOperation
);
}
export function* watchForUpdateAuthorities() {
yield* takeEvery(transactionActions.UPDATE_AUTHORITIES, updateAuthorities);
}
export function* watchForUpdateMeta() {
yield* takeEvery(transactionActions.UPDATE_META, updateMeta);
}
export function* watchForRecoverAccount() {
yield* takeEvery(transactionActions.RECOVER_ACCOUNT, recoverAccount);
}
const hook = {
preBroadcast_comment,
preBroadcast_transfer,
......@@ -181,7 +164,7 @@ function* error_account_witness_vote({
}
/** Keys, username, and password are not needed for the initial call. This will check the login and may trigger an action to prompt for the password / key. */
function* broadcastOperation({
export function* broadcastOperation({
payload: {
type,
operation,
......@@ -697,7 +680,7 @@ function slug(text) {
const pwPubkey = (name, pw, role) =>
auth.wifToPublic(auth.toWif(name, pw.trim(), role));
function* recoverAccount({
export function* recoverAccount({
payload: {
account_to_recover,
old_password,
......@@ -710,6 +693,7 @@ function* recoverAccount({
[api, api.getAccountsAsync],
[account_to_recover]
);
if (!account) {
onError('Unknown account ' + account);
return;
......@@ -760,6 +744,7 @@ function* recoverAccount({
};
try {
// TODO: Investigate wrapping in a redux-saga call fn, so it can be tested!.
yield broadcast.sendAsync(
{
extensions: [],
......@@ -780,6 +765,7 @@ function* recoverAccount({
// change password
// change password probably requires a separate transaction (single trx has not been tested)
const { json_metadata } = account;
// TODO: Investigate wrapping in a redux-saga call fn, so it can be tested!
yield broadcast.sendAsync(
{
extensions: [],
......@@ -806,6 +792,21 @@ function* recoverAccount({
},
[newOwnerPrivate]
);
// Reset all outgoing auto-vesting routes for this user. Condenser - #2835
const outgoingAutoVestingRoutes = yield call(
[api, api.getWithdrawRoutes],
[account.name, 'outgoing']
);
if (outgoingAutoVestingRoutes && outgoingAutoVestingRoutes.length > 0) {
yield all(
outgoingAutoVestingRoutes.map(ovr => {
return call(
[broadcast, broadcast.setWithdrawVestingRoute],
[newActive, ovr.from_account, ovr.to_account, 0, true]
);
})
);
}
if (onSuccess) onSuccess();
} catch (error) {
console.error('Recover account', error);
......@@ -815,7 +816,7 @@ function* recoverAccount({
/** auths must start with most powerful key: owner for example */
// const twofaAccount = 'steem'
function* updateAuthorities({
export function* updateAuthorities({
payload: { accountName, signingKey, auths, twofa, onSuccess, onError },
}) {
// Be sure this account is up-to-date (other required fields are sent in the update)
......@@ -955,7 +956,7 @@ function* updateAuthorities({
/** auths must start with most powerful key: owner for example */
// const twofaAccount = 'steem'
function* updateMeta(params) {
export function* updateMeta(params) {
// console.log('params', params)
const {
meta,
......
/* global describe, it, before, beforeEach, after, afterEach */
import { call, select } from 'redux-saga/effects';
import { api } from '@steemit/steem-js';
import { call, select, all, takeEvery } from 'redux-saga/effects';
import steem, { api, broadcast } from '@steemit/steem-js';
import { cloneableGenerator } from 'redux-saga/utils';
import * as transactionActions from 'app/redux/TransactionReducer';
import {
preBroadcast_comment,
createPermlink,
createPatch,
watchForBroadcast,
watchForUpdateAuthorities,
watchForUpdateMeta,
watchForRecoverAccount,
recoverAccount,
preBroadcast_transfer,
transactionWatches,
broadcastOperation,
updateAuthorities,
updateMeta,
} from './TransactionSaga';
import { DEBT_TICKER } from 'app/client_config';
......@@ -42,6 +45,137 @@ const operation = {
const username = 'Beatrice';
describe('TransactionSaga', () => {
describe('watch user actions and trigger appropriate saga', () => {
const gen = transactionWatches;
it('should call the broadcastOperation saga with every transactionActions.BROADCAST_OPERATION action', () => {
expect(gen).toEqual([
takeEvery(
transactionActions.BROADCAST_OPERATION,
broadcastOperation
),
takeEvery(
transactionActions.UPDATE_AUTHORITIES,
updateAuthorities
),
takeEvery(transactionActions.UPDATE_META, updateMeta),
takeEvery(transactionActions.RECOVER_ACCOUNT, recoverAccount),
]);
});
});
describe('recoverAccount', () => {
const gen = cloneableGenerator(recoverAccount)({
payload: {
account_to_recover: 'one',
old_password: 'two34567',
new_password: 'two34567',
onError: () => 'error!',
onSuccess: () => 'success!',
},
});
it('should call getAccountsAsync with account_to_recover username as argument', () => {
const actual = gen.next([{ id: 123, name: 'one' }]).value;
const mockCall = call([api, api.getAccountsAsync], ['one']);
expect(actual).toEqual(mockCall);
});
it('should call sendAsync with recover_account operation', () => {
const actual = gen.next([{ id: 123, name: 'one' }]).value;
const mockCall = broadcast.sendAsync(
{
extensions: [],
operations: [
[
'recover_account',
{
account_to_recover: 'one',
new_owner_authority: 'idk',
recent_owner_authority: 'something',
},
],
],
},
['123', '345']
);
expect(actual).toEqual(mockCall);
});
it('should call sendAsync with account_update operation', () => {
const actual = gen.next().value;
const mockCall = broadcast.sendAsync(
{
extensions: [],
operations: [
[
'account_update',
{
account_to_recover: 'one',
new_owner_authority: 'idk',
recent_owner_authority: 'something',
account: 'one',
active: {
weight_threshold: 1,
account_auths: [],
key_auths: [['newactive', 1]],
},
posting: {
weight_threshold: 1,
account_auths: [],
key_auths: [['newposting', 1]],
},
memo_key: 'newmemo',
},
],
],
},
['newownerprivate']
);
expect(actual).toEqual(mockCall);
});
it('should call getWithdrawRoutes with account name and outgoing as parameters', () => {
const noAutoVests = gen.clone();
const actual = noAutoVests.next().value;
const mockCall = call(
[api, api.getWithdrawRoutes],
['one', 'outgoing']
);
expect(actual).toEqual(mockCall);
const done = noAutoVests.next().done;
expect(done).toBe(true);
});
it('should call getWithdrawRoutes with account name and outgoing as parameters, and be done if none are found', () => {
const noAutoVests = gen.clone();
const actual = noAutoVests.next().value;
const mockCall = call(
[api, api.getWithdrawRoutes],
['one', 'outgoing']
);
expect(actual).toEqual(mockCall);
const done = noAutoVests.next().done;
expect(done).toBe(true);
});
it('should call getWithdrawRoutes with account name and outgoing as parameters, and reset all outgoing auto vesting routes to 0.', () => {
const withAutoVests = gen.clone();
withAutoVests.next([{ from_account: 'one', to_account: 'two' }])
.value;
const actual = withAutoVests.next([
{ from_account: 'one', to_account: 'two' },
]).value;
const mockCall = all([
call(
[broadcast, broadcast.setWithdrawVestingRoute],
[
'STM7UbRctdfcdBU6rMBEX5yPjWaR68xmq6buCkotR7RVEJHYWt1Jb',
'one',
'two',
0,
true,
]
),
]);
expect(actual).toEqual(mockCall);
});
});
describe('createPatch', () => {
it('should return undefined if empty arguments are passed', () => {
const actual = createPatch('', '');
......@@ -57,42 +191,6 @@ describe('TransactionSaga', () => {
});
});
describe('watchForBroadcast', () => {
const gen = watchForBroadcast();
it('should call takeEvery with BROADCAST_OPERATION', () => {
const actual = gen.next().value;
const expected = {
'@@redux-saga/IO': true,
TAKE: 'transaction/BROADCAST_OPERATION',
};
expect(actual).toEqual(expected);
});
});
describe('watchForUpdateAuthorities', () => {
const gen = watchForUpdateAuthorities();
it('should call takeEvery with UPDATE_AUTHORITIES', () => {
const actual = gen.next().value;
const expected = {
'@@redux-saga/IO': true,
TAKE: 'transaction/UPDATE_AUTHORITIES',
};
expect(actual).toEqual(expected);
});
});
describe('watchForUpdateMeta', () => {
const gen = watchForUpdateMeta();
it('should call takeEvery with UPDATE_META', () => {
const actual = gen.next().value;
const expected = {
'@@redux-saga/IO': true,
TAKE: 'transaction/UPDATE_META',
};
expect(actual).toEqual(expected);
});
});
describe('preBroadcast_transfer', () => {
const operationSansMemo = {
...operation,
......
import { fromJS, Set, List } from 'immutable';
import { takeLatest } from 'redux-saga';
import { call, put, select, fork } from 'redux-saga/effects';
import { call, put, select, fork, takeLatest } from 'redux-saga/effects';
import { api } from '@steemit/steem-js';
import { PrivateKey, Signature, hash } from '@steemit/steem-js/lib/auth/ecc';
......@@ -21,17 +20,34 @@ import { translate } from 'app/Translator';
import DMCAUserList from 'app/utils/DMCAUserList';
export const userWatches = [
watchRemoveHighSecurityKeys, // keep first to remove keys early when a page change happens
loginWatch,
saveLoginWatch,
logoutWatch,
// getCurrentAccountWatch,
loginErrorWatch,
lookupPreviousOwnerAuthorityWatch,
watchLoadSavingsWithdraw,
uploadImageWatch,
acceptTosWatch,
getLatestFeedPrice,
takeLatest('@@router/LOCATION_CHANGE', removeHighSecurityKeys), // keep first to remove keys early when a page change happens
takeLatest(
'user/lookupPreviousOwnerAuthority',
lookupPreviousOwnerAuthority
),
takeLatest(userActions.USERNAME_PASSWORD_LOGIN, usernamePasswordLogin),
takeLatest(userActions.SAVE_LOGIN, saveLogin_localStorage),
takeLatest(userActions.LOGOUT, logout),
takeLatest(userActions.LOGIN_ERROR, loginError),
takeLatest(userActions.LOAD_SAVINGS_WITHDRAW, loadSavingsWithdraw),
takeLatest(userActions.UPLOAD_IMAGE, uploadImage),
takeLatest(userActions.ACCEPT_TERMS, function*() {
try {
yield call(acceptTos);
} catch (e) {
// TODO: log error to server, conveyor is unavailable
}
}),
function* getLatestFeedPrice() {
try {
const history = yield call([api, api.getFeedHistoryAsync]);
const feed = history['price_history'];
const last = fromJS(feed[feed.length - 1]);
yield put(userActions.setLatestFeedPrice(last));
} catch (error) {
// (exceedingly rare) ignore, UI will fall back to feed_price
}
},
];
const highSecurityPages = [
......@@ -40,48 +56,6 @@ const highSecurityPages = [
/\/~witnesses/,
];
function* lookupPreviousOwnerAuthorityWatch() {
yield* takeLatest(
'user/lookupPreviousOwnerAuthority',
lookupPreviousOwnerAuthority
);
}
function* loginWatch() {
yield* takeLatest(
userActions.USERNAME_PASSWORD_LOGIN,
usernamePasswordLogin
);
}
function* saveLoginWatch() {
yield* takeLatest(userActions.SAVE_LOGIN, saveLogin_localStorage);
}
function* logoutWatch() {
yield* takeLatest(userActions.LOGOUT, logout);
}
function* loginErrorWatch() {
yield* takeLatest(userActions.LOGIN_ERROR, loginError);
}