Skip to content

Commit

Permalink
Reduce API calls from certain elements by keeping global state up-to-…
Browse files Browse the repository at this point in the history
…date and caching redundant data (#126)

* Refactor SET_FEE_INFO out of SET_NODE_INFO

* Introduce NEW_BLOCK_STATUS action and state property

* Display newBlockStatus in sidebar footer

* Move getFees to onNewBlock() and dispatch progress

* Await selectwallet before executing rpc

* Run onNewBlock() on app startup and remove redundant fetchWallet()

fetchWallet() is already called in startNode aka start()
in ducks/node.js

* Get all bid and namestate data from store instead of each component

Bid and namestate data will be updated on every new block and stored
globally instead of being retrieved by each component individually.

* Do not execute doNewBlock on uninitialized wallet

* Update changelog

* Use chain tip block hash instead of height to trigger onNewBlock()

* Removed unused aggregate balance property of TXs in fetchTransactions

* Use Map for state.wallet.transactions

TXs are still sorted by date but this way we can key into the TX
history by txid and avoid requesting data we already have.

* Use state.wallet.transactions Map to avoid reprocessing TX data
  • Loading branch information
pinheadmz committed Apr 28, 2020
1 parent 2df1f9d commit 46730f9
Show file tree
Hide file tree
Showing 16 changed files with 115 additions and 70 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Fixed
- Fixed total amount received by a transaction with multiple outputs to the wallet
- Fixed sluggish UI from mass of redundant rpc calls on "Your Bids" view

### Added
- Added warning for missing bid values and functionality to repair missing bids
Expand Down
2 changes: 1 addition & 1 deletion app/background/wallet/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ class WalletService {
};

async _executeRPC(method, args) {
this._selectWallet();
await this._selectWallet();
return this.client.execute(method, args);
}
}
Expand Down
9 changes: 7 additions & 2 deletions app/components/Sidebar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { Logo } from '../Logo';
@connect(
state => ({
height: state.node.chain.height,
tip: state.node.chain.tip
tip: state.node.chain.tip,
newBlockStatus: state.node.newBlockStatus,
}),
dispatch => ({})
)
Expand All @@ -23,7 +24,8 @@ class Sidebar extends Component {
pathname: PropTypes.string.isRequired
}).isRequired,
height: PropTypes.number.isRequired,
tip: PropTypes.string.isRequired
tip: PropTypes.string.isRequired,
newBlockStatus: PropTypes.string.isRequired,
};

render() {
Expand Down Expand Up @@ -118,6 +120,9 @@ class Sidebar extends Component {
renderFooter() {
return (
<div className="sidebar__footer">
<div className="sidebar__footer__row">
<div className="sidebar__footer__title">{this.props.newBlockStatus}</div>
</div>
<div className="sidebar__footer__row">
<div className="sidebar__footer__title">Current Height</div>
<div className="sidebar__footer__text">
Expand Down
3 changes: 2 additions & 1 deletion app/components/Sidebar/sidebar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@

&__footer {
@extend %col-nowrap;
padding: 1rem 0;
bottom: 1rem;
position: relative;

&__row {
@extend %col-nowrap;
Expand Down
6 changes: 3 additions & 3 deletions app/components/Transactions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ const ITEM_PER_DROPDOWN = [
)
export default class Transactions extends Component {
static propTypes = {
transactions: PropTypes.array.isRequired
transactions: PropTypes.instanceOf(Map).isRequired
};

async componentDidMount() {
await this.props.fetchTransactions()
}

componentWillReceiveProps(nextProps) {
if (this.props.transactions.length !== nextProps.transactions.length) {
if (this.props.transactions.size !== nextProps.transactions.size) {
this.fuse = null;
}
}
Expand All @@ -62,7 +62,7 @@ export default class Transactions extends Component {

getTransactions() {
const { sortBy, query } = this.state;
let transactions = this.props.transactions.slice();
let transactions = Array.from(this.props.transactions.values());

if (!this.fuse) {
this.fuse = new Fuse(transactions, {
Expand Down
49 changes: 42 additions & 7 deletions app/ducks/backgroundMonitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import { store } from '../store/configureStore';
import { LOCK_WALLET, SET_PENDING_TRANSACTIONS } from './walletReducer';
import { getInitializationState } from '../db/system';
import isEqual from 'lodash.isequal';
import { SET_NODE_INFO } from './nodeReducer';
import { SET_NODE_INFO, SET_FEE_INFO, NEW_BLOCK_STATUS } from './nodeReducer';
import { getNameInfo } from './names';
import { getYourBids } from './bids';
import { fetchTransactions, fetchWallet } from './walletActions';

export function createBackgroundMonitor() {
let isLocked;
let timeout;
let info;
let fees;
let prevNamesWithPendingUpdates = new Set();

const doPoll = async () => {
Expand Down Expand Up @@ -44,20 +44,19 @@ export function createBackgroundMonitor() {
chain: infoRes.chain,
network: infoRes.network,
};

const newFees = await nodeClient.getFees();
if (!isEqual(info, newInfo) || !isEqual(fees, newFees)) {
if (!isEqual(info, newInfo)) {
info = newInfo;
fees = newFees;
store.dispatch({
type: SET_NODE_INFO,
payload: {
info: newInfo,
fees: newFees,
},
});
}

if (state.node.chain.tip !== infoRes.chain.tip)
await store.dispatch(onNewBlock());

const newPendingTxns = await walletClient.getPendingTransactions();
store.dispatch({
type: SET_PENDING_TRANSACTIONS,
Expand Down Expand Up @@ -115,4 +114,40 @@ function difference(setA, setB) {
return d;
}

export const onNewBlock = () => async (dispatch, getState) => {
let state = getState();
const isInitialized = await getInitializationState(state.node.network);
if (!isInitialized) {
return;
}

if (!state.wallet.initialized || !state.node.isRunning) {
return;
}

dispatch({type: NEW_BLOCK_STATUS, payload: 'Updating fees...'});
const newFees = await nodeClient.getFees();
if (!isEqual(state.node.fees, newFees)) {
dispatch({
type: SET_FEE_INFO,
payload: {
fees: newFees,
},
});
}

dispatch({type: NEW_BLOCK_STATUS, payload: 'Loading bids...'});
await dispatch(getYourBids());

state = getState();
const bids = state.bids.yourBids;
for (const bid of bids) {
const name = bid.name;
dispatch({type: NEW_BLOCK_STATUS, payload: `Loading name: ${name}`});
await dispatch(getNameInfo(name));
}

dispatch({type: NEW_BLOCK_STATUS, payload: ''});
}

export const monitor = createBackgroundMonitor();
15 changes: 14 additions & 1 deletion app/ducks/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ import { clientStub } from '../background/node/client';
import { getNetwork, setNetwork } from '../db/system';
import { fetchWallet } from './walletActions';
import * as logger from '../utils/logClient';
import { END_NETWORK_CHANGE, SET_NODE_INFO, START, START_ERROR, START_NETWORK_CHANGE, STOP } from './nodeReducer';
import {
END_NETWORK_CHANGE,
SET_NODE_INFO,
SET_FEE_INFO,
START,
START_ERROR,
START_NETWORK_CHANGE,
STOP,
} from './nodeReducer';
import { VALID_NETWORKS } from '../constants/networks';

const nodeClient = clientStub(() => require('electron').ipcRenderer);
Expand Down Expand Up @@ -47,6 +55,11 @@ const setNodeInfo = () => async (dispatch) => {
type: SET_NODE_INFO,
payload: {
info,
},
});
dispatch({
type: SET_FEE_INFO,
payload: {
fees,
},
});
Expand Down
14 changes: 13 additions & 1 deletion app/ducks/nodeReducer.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
export const START = 'node/START';
export const START_ERROR = 'node/START_ERROR';
export const SET_NODE_INFO = 'node/SET_NODE_INFO';
export const SET_FEE_INFO = 'node/SET_FEE_INFO';
export const STOP = 'node/STOP';
export const START_NETWORK_CHANGE = 'node/START_NETWORK_CHANGE';
export const END_NETWORK_CHANGE = 'node/END_NETWORK_CHANGE';
export const NEW_BLOCK_STATUS = 'node/NEW_BLOCK_STATUS';

export function getInitialState() {
return {
Expand All @@ -20,7 +22,8 @@ export function getInitialState() {
chain: {
height: 0,
tip: '',
}
},
newBlockStatus: ''
};
}

Expand All @@ -36,6 +39,10 @@ export default function nodeReducer(state = getInitialState(), action = {}) {
return {
...state,
chain: action.payload.info.chain,
};
case SET_FEE_INFO:
return {
...state,
fees: action.payload.fees,
};
case START_NETWORK_CHANGE:
Expand All @@ -44,6 +51,11 @@ export default function nodeReducer(state = getInitialState(), action = {}) {
...state,
isChangingNetworks: action.type === START_NETWORK_CHANGE
};
case NEW_BLOCK_STATUS:
return {
...state,
newBlockStatus: action.payload,
};
default:
return state;
}
Expand Down
37 changes: 21 additions & 16 deletions app/ducks/walletActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
STOP_SYNC_WALLET,
SYNC_WALLET_PROGRESS,
} from './walletReducer';
import {NEW_BLOCK_STATUS} from './nodeReducer';

let idleInterval;

Expand Down Expand Up @@ -157,35 +158,39 @@ export const waitForWalletSync = () => async (dispatch, getState) => {
};

export const fetchTransactions = () => async (dispatch, getState) => {
const net = getState().node.network;
const state = getState();
const net = state.node.network;
const currentTXs = state.wallet.transactions;
const txs = await walletClient.getTransactionHistory();
let aggregate = new BigNumber(0);
let payload = [];
let payload = new Map();

for (const tx of txs) {
for (let i = 0; i < txs.length; i++) {
const tx = txs[i];
const existing = currentTXs.get(tx.hash);
if (existing) {
const isPending = tx.block === null;
existing.date = isPending ? Date.now() : Date.parse(tx.date);
existing.pending = isPending;

payload.set(existing.id, existing);
continue;
}
dispatch({type: NEW_BLOCK_STATUS, payload: `Processing TX: ${i}/${txs.length}`});
const ios = await parseInputsOutputs(net, tx);
aggregate =
ios.type !== 'RECEIVE'
? aggregate.minus(ios.value)
: aggregate.plus(ios.value);
const isPending = tx.block === null;
const txData = {
id: tx.hash,
date: isPending ? Date.now() : Date.parse(tx.date),
pending: isPending,
balance: aggregate.toString(),
...ios,
};

if (isPending) {
payload.unshift(txData);
continue;
}

payload.push(txData);
payload.set(tx.hash, txData);
}
dispatch({type: NEW_BLOCK_STATUS, payload: ''});

payload = payload.sort((a, b) => b.date - a.date);
// Sort all TXs by date without losing the hash->tx mapping
payload = new Map([...payload.entries()].sort((a, b) => b[1].date - a[1].date));

dispatch({
type: SET_TRANSACTIONS,
Expand Down
4 changes: 2 additions & 2 deletions app/ducks/walletReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function getInitialState() {
lockedConfirmed: 0,
lockedUnconfirmed: 0,
},
transactions: [],
transactions: new Map(),
idle: 0,
walletSync: false,
walletSyncProgress: 0,
Expand Down Expand Up @@ -59,7 +59,7 @@ export default function walletReducer(state = getInitialState(), {type, payload}
...state.balance
},
isLocked: true,
transactions: []
transactions: new Map()
};
case UNLOCK_WALLET:
return {
Expand Down
7 changes: 4 additions & 3 deletions app/pages/App/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import * as walletActions from '../../ducks/walletActions';
import './app.scss';
import AccountLogin from '../AcountLogin';
import * as node from '../../ducks/node';
import { onNewBlock } from '../../ducks/backgroundMonitor';
import Notification from '../../components/Notification';
import SplashScreen from "../../components/SplashScreen";
import WalletSync from "../../components/WalletSync";
Expand All @@ -37,8 +38,8 @@ class App extends Component {
error: PropTypes.string.isRequired,
isLocked: PropTypes.bool.isRequired,
initialized: PropTypes.bool.isRequired,
fetchWallet: PropTypes.func.isRequired,
startNode: PropTypes.func.isRequired,
onNewBlock: PropTypes.func.isRequired,
watchActivity: PropTypes.func.isRequired,
isChangingNetworks: PropTypes.bool.isRequired,
};
Expand All @@ -50,7 +51,7 @@ class App extends Component {
async componentDidMount() {
this.setState({isLoading: true});
await this.props.startNode();
await this.props.fetchWallet();
await this.props.onNewBlock();
this.props.watchActivity();
setTimeout(() => this.setState({isLoading: false}), 1000)
}
Expand Down Expand Up @@ -229,9 +230,9 @@ export default withRouter(
initialized: state.wallet.initialized
}),
dispatch => ({
fetchWallet: () => dispatch(walletActions.fetchWallet()),
watchActivity: () => dispatch(walletActions.watchActivity()),
startNode: () => dispatch(node.start()),
onNewBlock: () => dispatch(onNewBlock()),
})
)(App)
);
6 changes: 3 additions & 3 deletions app/pages/Settings/ZapTXsModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {showError, showSuccess } from '../../ducks/notifications';
)
class ZapTXsModal extends Component {
static propTypes = {
transactions: PropTypes.array.isRequired,
transactions: PropTypes.instanceOf(Map).isRequired,
showError: PropTypes.func.isRequired,
showSuccess: PropTypes.func.isRequired,
};
Expand Down Expand Up @@ -51,10 +51,10 @@ class ZapTXsModal extends Component {
if (this.props.transactions) {
const txs = [];

for (const tx of this.props.transactions) {
this.props.transactions.forEach( tx => {
if (tx.pending)
txs.push(tx);
}
});

if (txs.length === 0) {
txList.push(
Expand Down

0 comments on commit 46730f9

Please sign in to comment.