Skip to content

Commit

Permalink
device and hub actions separated from wallet logic, pairing code gene…
Browse files Browse the repository at this point in the history
…ration added
  • Loading branch information
DanielLaanpere committed Oct 30, 2019
1 parent 0ab9ba1 commit 1048f96
Show file tree
Hide file tree
Showing 15 changed files with 209 additions and 142 deletions.
5 changes: 5 additions & 0 deletions src/actions/device.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { actionTypes } from './../constants';

export const rotateDeviceTempKey = () => ({
type: actionTypes.DEVICE_TEMP_KEY_ROTATE,
});
19 changes: 16 additions & 3 deletions src/components/ChatListScreen/ActionsBar.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
import React from 'react';
import Crypto from 'crypto';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { TouchableOpacity } from 'react-native';
import { View } from 'native-base';
import { withNavigation } from 'react-navigation';
import { selectDeviceAddress } from './../../selectors/wallet';
import { selectDevicePubKey } from './../../selectors/device';
import { hubAddress, urlHost } from './../../lib/OCustom';
import AddContactIcon from './../../assets/images/icon-person-add.svg';
import ScanIcon from './../../assets/images/icon-scan.svg';
import QRIcon from './../../assets/images/icon-qr.svg';
import styles from './styles';


class ActionsBar extends React.Component {
constructor(props) {
super(props);
this._getPairingCode = this._getPairingCode.bind(this);
}

_getPairingCode() {
const pairingSecret = Crypto.randomBytes(9).toString('base64');
pairingCode = `${this.props.devicePubKey}@${hubAddress}#${pairingSecret}`;
return pairingCode;
}

render() {
return (
<View style={styles.actionsBar}>
Expand All @@ -23,7 +36,7 @@ class ActionsBar extends React.Component {
<TouchableOpacity
style={styles.iconButton}
onPress={() => this.props.navigation.navigate('MyQR', {
qrData: `obyte-tn:${this.props.deviceAddress}`,
qrData: `${urlHost}${this._getPairingCode()}`,
})}
>
<QRIcon style={styles.icon} width={15} height={15} />
Expand All @@ -37,7 +50,7 @@ class ActionsBar extends React.Component {
}

const mapStateToProps = createStructuredSelector({
deviceAddress: selectDeviceAddress(),
devicePubKey: selectDevicePubKey(),
});

const mapDispatchToProps = dispatch => ({});
Expand Down
2 changes: 1 addition & 1 deletion src/components/PaymentScreen/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { colors } from '../../constants';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { selectExchangeRates } from './../../selectors/exchangeRates';
import { sendPaymentStart } from './../../actions/wallet';
import { availableUnits, bytesToUnit, unitToBytes } from './../../lib/Wallet';
import { availableUnits, unitToBytes } from './../../lib/Wallet';


class PaymentScreen extends React.Component {
Expand Down
3 changes: 2 additions & 1 deletion src/constants/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export const SETTINGS_RESET = 'SETTINGS_RESET';
export const PAYMENT_SEND_START = 'PAYMENT_SEND_START';
export const PAYMENT_SEND_SUCCESS = 'PAYMENT_SEND_SUCCESS';
export const PAYMENT_SEND_FAILED = 'PAYMENT_SEND_FAILED';
export const APP_TOAST_SET = 'APP_TOAST_SET';
export const APP_TOAST_SET = 'APP_TOAST_SET';
export const DEVICE_TEMP_KEY_ROTATE = 'DEVICE_TEMP_KEY_ROTATE';
16 changes: 16 additions & 0 deletions src/lib/OCustom.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import _ from 'lodash';
import Crypto from 'crypto';
import obyte from 'obyte';
import { common } from './../constants';


export const testnet = common.network === 'testnet';

export const hubAddress = common.network === 'testnet'
? 'obyte.org/bb-test'
: 'obyte.org/bb';

export const oClient = common.network === 'testnet'
? new obyte.Client('wss://obyte.org/bb-test', { testnet })
: new obyte.Client('wss://obyte.org/bb');

export const urlHost = common.network === 'testnet'
? 'obyte-tn:'
: 'obyte:';

export const getDeviceMessageHashToSign = (objDeviceMessage) => {
var objNakedDeviceMessage = _.clone(objDeviceMessage);
delete objNakedDeviceMessage.signature;
Expand Down
10 changes: 0 additions & 10 deletions src/lib/Wallet.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
import obyte from 'obyte';
import { common } from './../constants';


export const testnet = common.network === 'testnet';

export const oClient = common.network === 'testnet'
? new obyte.Client('wss://obyte.org/bb-test', { testnet })
: new obyte.Client('wss://obyte.org/bb');

/**
* Converts bytes to other sizes
* @param {int} n - in bytes (B)
Expand Down
21 changes: 0 additions & 21 deletions src/reducers/app.js

This file was deleted.

38 changes: 38 additions & 0 deletions src/reducers/device.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { REHYDRATE } from 'redux-persist';
import Crypto from 'crypto';
import { actionTypes } from '../constants';


const initialState = {
deviceTempKeys: {
prevPrivKey: Crypto.randomBytes(32),
privKey: Crypto.randomBytes(32),
rotationTimestamp: Date.now(),
}
};

function reducer(state = initialState, action) {
switch (action.type) {
case REHYDRATE:
return {
...state,
...action.payload.app,
};

case actionTypes.DEVICE_TEMP_KEY_ROTATE:
return {
...state,
deviceTempKeys: {
privKey: Crypto.randomBytes(32),
privKey: action.payload.privKey,
prevPrivKey: state.deviceTempKeys.privKey,
rotationTimestamp: Date.now(),
}
};

default:
return state;
}
}

export default reducer;
70 changes: 70 additions & 0 deletions src/sagas/device.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { take, takeEvery, call, put, select } from '@redux-saga/core/effects';
import { channel } from '@redux-saga/core';
import { sign } from 'obyte/lib/internal';
import { oClient, getDeviceMessageHashToSign } from './../lib/OCustom';
import { actionTypes } from './../constants';
import { setExchangeRates } from './../actions/exchangeRates';
import {
selectPermanentDeviceKeyObj,
selectDeviceAddress,
} from './../selectors/device';


export const oChannel = channel();

export function* subscribeToHub() {
try {
oClient.subscribe((err, result) => {
if (err) {
throw new Error('Hub socket error');
} else {
const [messageType, message] = result;
oChannel.put({ type: messageType, payload: message });
}
});
yield call(setInterval, () => oClient.api.heartbeat(), 10 * 1000);
} catch (error) {
yield put(setToastMessage({
type: 'ERROR',
message: 'Hub connection error',
}));
console.log(error);
}
}

export function* watchHubMessages() {
while (true) {
const { type, payload } = yield take(oChannel)

if (type === 'justsaying') {
switch (payload.subject) {
case 'hub/challenge':
yield call(loginToHub, payload.body);
return;
default:
console.log(payload.body);
}
}
}
}

export function* loginToHub(challenge) {
const permanentDeviceKey = yield select(selectPermanentDeviceKeyObj());

const objLogin = { challenge, pubkey: permanentDeviceKey.pub_b64 };
objLogin.signature = sign(
getDeviceMessageHashToSign(objLogin),
permanentDeviceKey.priv,
);
oClient.justsaying('hub/login', objLogin);

}

export function* rotateDeviceTempKey() {
const ROTATION_PERIOD = 3600 * 1000;
}

export default function* watch() {
yield takeEvery(actionTypes.DEVICE_TEMP_KEY_ROTATE, rotateDeviceTempKey);
yield watchHubMessages();
}
2 changes: 2 additions & 0 deletions src/sagas/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { all } from '@redux-saga/core/effects';
import appSaga from './app';
import walletSaga from './wallet';
import deviceSaga from './device';


export default function* rootSaga() {
yield all([
appSaga(),
walletSaga(),
deviceSaga(),
]);
}
70 changes: 6 additions & 64 deletions src/sagas/wallet.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { takeLatest, takeEvery, call, put, take, select } from '@redux-saga/core/effects';
import { channel } from '@redux-saga/core';
import { takeLatest, takeEvery, call, put, select } from '@redux-saga/core/effects';
import Mnemonic from 'bitcore-mnemonic';
import Crypto from 'crypto';
import { sign } from 'obyte/lib/internal';
import NavigationService from './../navigation/service';
import { oClient, testnet } from './../lib/Wallet';
import { getDeviceMessageHashToSign } from './../lib/OCustom';
import { oClient } from './../lib/OCustom';
import { actionTypes } from './../constants';
import { setToastMessage } from './../actions/app';
import { subscribeToHub } from './device';
import {
createInitialWalletStart,
createInitialWalletSuccess,
Expand All @@ -23,7 +20,6 @@ import {
loadWalletHistorySuccess,
loadWalletHistoryFail,
} from '../actions/walletHistory';
import { setExchangeRates } from './../actions/exchangeRates';
import {
loadWalletBalancesSuccess,
loadWalletBalancesFail,
Expand All @@ -33,12 +29,9 @@ import {
selectWalletAddress,
selectWalletWif,
selectWitnesses,
selectPermanentDeviceKeyObj,
} from './../selectors/wallet';


export const oChannel = channel();

export function* initWallet(action) {
try {
const walletData = yield select(selectWallet());
Expand All @@ -51,8 +44,8 @@ export function* initWallet(action) {
// Fetch wallet data from hub
yield call(fetchBalances, action);
yield call(fetchWitnesses, action);
yield call(fetchWalletHistory, action);
yield put(initWalletSuccess({}));
yield put(loadWalletHistory());
yield put(initWalletSuccess());
} catch (error) {
yield put(initWalletFail());
yield put(setToastMessage({
Expand Down Expand Up @@ -170,60 +163,9 @@ export function* sendPayment(action) {
}
}

export function* subscribeToHub(action) {
try {
oClient.subscribe((err, result) => {
if (err) {
throw new Error('Hub socket error');
} else {
const [messageType, message] = result;
oChannel.put({ type: messageType, payload: message });
}
});
yield call(setInterval, () => oClient.api.heartbeat(), 10 * 1000);
} catch (error) {
yield put(setToastMessage({
type: 'ERROR',
message: 'Hub connection error',
}));
console.log(error);
}
}

export function* watchHubMessages() {
while (true) {
const { type, payload } = yield take(oChannel)

if (type === 'justsaying') {
switch (payload.subject) {
case 'hub/challenge':
yield call(loginToHub, payload.body);
return;
default:
console.log(payload.body);
}
}
}
}

export function* loginToHub(challenge) {
const permanentDeviceKey = yield select(selectPermanentDeviceKeyObj());
const deviceTempPrivKey = Crypto.randomBytes(32);
const devicePrevTempPrivKey = Crypto.randomBytes(32);

const objLogin = { challenge, pubkey: permanentDeviceKey.pub_b64 };
objLogin.signature = sign(
getDeviceMessageHashToSign(objLogin),
permanentDeviceKey.priv,
);
oClient.justsaying('hub/login', objLogin);

}

export default function* walletSagas() {
export default function* watch() {
yield takeLatest(actionTypes.WALLET_INIT_START, initWallet);
yield takeLatest(actionTypes.INITIAL_WALLET_CREATE_START, createInitialWallet);
yield takeLatest(actionTypes.WALLET_BALANCES_FETCH_START, fetchBalances);
yield takeEvery(actionTypes.PAYMENT_SEND_START, sendPayment);
yield watchHubMessages();
}
4 changes: 0 additions & 4 deletions src/selectors/app.js

This file was deleted.

Loading

0 comments on commit 1048f96

Please sign in to comment.