Merge pull request #1949 from BlueWallet/fix-freeze-send-screen

FIX: rare freezes on send screen (closes #1945)
This commit is contained in:
Overtorment 2020-10-06 06:58:55 +01:00 committed by GitHub
commit 9fc605893d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 100 additions and 38 deletions

View File

@ -177,18 +177,8 @@ const SendDetailsRoot = () => (
component={PsbtWithHardwareWallet}
options={PsbtWithHardwareWallet.navigationOptions}
/>
<SendDetailsStack.Screen name="CreateTransaction" component={SendCreate} options={SendCreate.navigationOptions} />
<SendDetailsStack.Screen name="PsbtMultisig" component={PsbtMultisig} options={PsbtMultisig.navigationOptions} />
<SendDetailsStack.Screen
name="CreateTransaction"
component={SendCreate}
options={{
headerStyle: {
backgroundColor: '#FFFFFF',
borderBottomWidth: 0,
},
headerTintColor: '#0c2550',
}}
/>
<SendDetailsStack.Screen name="Success" component={Success} options={Success.navigationOptions} />
<SendDetailsStack.Screen name="SelectWallet" component={SelectWallet} options={SelectWallet.navigationOptions} />
</SendDetailsStack.Navigator>

View File

@ -680,8 +680,9 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
addressess = [...new Set(addressess)]; // deduplicate just for any case
const fetchedUtxo = await BlueElectrum.multiGetUtxoByAddress(addressess);
this._utxo = [];
for (const arr of Object.values(await BlueElectrum.multiGetUtxoByAddress(addressess))) {
for (const arr of Object.values(fetchedUtxo)) {
this._utxo = this._utxo.concat(arr);
}
@ -739,8 +740,6 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
}
if (ownedAddressesHashmap[address]) {
const value = new BigNumber(output.value).multipliedBy(100000000).toNumber();
const wif = returnSpentUtxoAsWell ? false : this._getWifForAddress(address);
// ^^^ faster, as we probably dont need WIFs for spent UTXO
utxos.push({
txid: tx.txid,
txId: tx.txid,
@ -749,7 +748,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
value,
amount: value,
confirmations: tx.confirmations,
wif,
wif: false,
height: BlueElectrum.estimateCurrentBlockheight() - tx.confirmations,
});
}
@ -769,7 +768,11 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
}
}
if (!spent) ret.push(utxo);
if (!spent) {
// filling WIFs only for legit unspent UTXO, as it is a slow operation
utxo.wif = this._getWifForAddress(utxo.address);
ret.push(utxo);
}
}
return ret;

View File

@ -26,6 +26,10 @@ export class AbstractHDWallet extends LegacyWallet {
return this.next_free_address_index;
}
getNextFreeChangeAddressIndex() {
return this.next_free_change_address_index;
}
prepareForSerialization() {
// deleting structures that cant be serialized
delete this._node0;
@ -93,7 +97,7 @@ export class AbstractHDWallet extends LegacyWallet {
if (!freeAddress) {
// could not find in cycle above, give up
freeAddress = this._getExternalAddressByIndex(this.next_free_address_index + c); // we didnt check this one, maybe its free
this.next_free_address_index += c + 1; // now points to the one _after_
this.next_free_address_index += c; // now points to this one
}
this._address = freeAddress;
return freeAddress;
@ -130,8 +134,8 @@ export class AbstractHDWallet extends LegacyWallet {
if (!freeAddress) {
// could not find in cycle above, give up
freeAddress = this._getExternalAddressByIndex(this.next_free_address_index + c); // we didnt check this one, maybe its free
this.next_free_address_index += c + 1; // now points to the one _after_
freeAddress = this._getInternalAddressByIndex(this.next_free_change_address_index + c); // we didnt check this one, maybe its free
this.next_free_change_address_index += c; // now points to this one
}
this._address = freeAddress;
return freeAddress;

View File

@ -219,6 +219,10 @@ export class AbstractWallet {
return new Promise(resolve => resolve(this.getAddress()));
}
async getChangeAddressAsync() {
return new Promise(resolve => resolve(this.getAddress()));
}
useWithHardwareWalletEnabled() {
return false;
}

View File

@ -401,10 +401,4 @@ export class LegacyWallet extends AbstractWallet {
allowSendMax() {
return true;
}
async getChangeAddressAsync() {
return new Promise(resolve => {
resolve(this.getAddress());
});
}
}

View File

@ -125,11 +125,21 @@ export class WatchOnlyWallet extends LegacyWallet {
throw new Error('Not initialized');
}
_getInternalAddressByIndex(index) {
if (this._hdWalletInstance) return this._hdWalletInstance._getInternalAddressByIndex(index);
throw new Error('Not initialized');
}
getNextFreeAddressIndex() {
if (this._hdWalletInstance) return this._hdWalletInstance.next_free_address_index;
throw new Error('Not initialized');
}
getNextFreeChangeAddressIndex() {
if (this._hdWalletInstance) return this._hdWalletInstance.next_free_change_address_index;
throw new Error('Not initialized');
}
async getChangeAddressAsync() {
if (this._hdWalletInstance) return this._hdWalletInstance.getChangeAddressAsync();
throw new Error('Not initialized');

View File

@ -20,7 +20,7 @@ export class NetworkTransactionFee {
export default class NetworkTransactionFees {
static recommendedFees() {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
return new Promise(async resolve => {
try {
const response = await BlueElectrum.estimateFees();
if (typeof response === 'object') {
@ -28,12 +28,12 @@ export default class NetworkTransactionFees {
resolve(networkFee);
} else {
const networkFee = new NetworkTransactionFee(1, 1, 1);
reject(networkFee);
resolve(networkFee);
}
} catch (err) {
console.warn(err);
const networkFee = new NetworkTransactionFee(1, 1, 1);
reject(networkFee);
resolve(networkFee);
}
});
}

View File

@ -253,8 +253,8 @@ SendCreate.navigationOptions = ({ navigation, route }) => {
}
return {
...BlueNavigationStyle,
title: loc.send.create.details,
...BlueNavigationStyle(),
title: loc.send.create_details,
headerRight,
};
};

View File

@ -44,6 +44,7 @@ import DocumentPicker from 'react-native-document-picker';
import DeeplinkSchemaMatch from '../../class/deeplink-schema-match';
import loc from '../../loc';
import { BlueCurrentTheme } from '../../components/themes';
import { AbstractHDElectrumWallet } from '../../class/wallets/abstract-hd-electrum-wallet';
const currency = require('../../blue_modules/currency');
const BlueApp: AppStorage = require('../../BlueApp');
const prompt = require('../../blue_modules/prompt');
@ -362,10 +363,10 @@ export default class SendDetails extends Component {
}
} catch (_) {}
await this.reCalcTx();
this.reCalcTx();
try {
const recommendedFees = await NetworkTransactionFees.recommendedFees();
const recommendedFees = await Promise.race([NetworkTransactionFees.recommendedFees(), BlueApp.sleep(2000)]);
if (recommendedFees && 'fastestFee' in recommendedFees) {
await AsyncStorage.setItem(NetworkTransactionFee.StorageKey, JSON.stringify(recommendedFees));
this.setState({
@ -373,7 +374,7 @@ export default class SendDetails extends Component {
networkTransactionFees: recommendedFees,
});
}
} catch (_) {}
} catch (_) {} // either sleep expired or recommendedFees threw an exception
if (this.props.route.params.uri) {
try {
@ -386,9 +387,15 @@ export default class SendDetails extends Component {
}
}
await this.state.fromWallet.fetchUtxo();
try {
await Promise.race([this.state.fromWallet.fetchUtxo(), BlueApp.sleep(6000)]);
} catch (_) {
// either sleep expired or fetchUtxo threw an exception
}
this.setState({ isLoading: false });
await this.reCalcTx();
this.reCalcTx();
}
componentWillUnmount() {
@ -467,13 +474,63 @@ export default class SendDetails extends Component {
}
}
getChangeAddressFast() {
if (this.state.changeAddress) return this.state.changeAddress; // cache
/** @type {AbstractHDElectrumWallet|WatchOnlyWallet} */
const wallet = this.state.fromWallet;
let changeAddress;
if (WatchOnlyWallet.type === wallet.type && !wallet.isHd()) {
// plain watchonly - just get the address
changeAddress = wallet.getAddress();
} else if (WatchOnlyWallet.type === wallet.type || wallet instanceof AbstractHDElectrumWallet) {
changeAddress = wallet._getInternalAddressByIndex(wallet.getNextFreeChangeAddressIndex());
} else {
// legacy wallets
changeAddress = wallet.getAddress();
}
return changeAddress;
}
async getChangeAddressAsync() {
if (this.state.changeAddress) return this.state.changeAddress; // cache
/** @type {AbstractHDElectrumWallet|WatchOnlyWallet} */
const wallet = this.state.fromWallet;
let changeAddress;
if (WatchOnlyWallet.type === wallet.type && !wallet.isHd()) {
// plain watchonly - just get the address
changeAddress = wallet.getAddress();
} else {
// otherwise, lets call widely-used getChangeAddressAsync()
try {
changeAddress = await Promise.race([BlueApp.sleep(2000), wallet.getChangeAddressAsync()]);
} catch (_) {}
if (!changeAddress) {
// either sleep expired or getChangeAddressAsync threw an exception
if (wallet instanceof AbstractHDElectrumWallet) {
changeAddress = wallet._getInternalAddressByIndex(wallet.getNextFreeChangeAddressIndex());
} else {
// legacy wallets
changeAddress = wallet.getAddress();
}
}
}
if (changeAddress) this.setState({ changeAddress }); // cache
return changeAddress;
}
/**
* Recalculating fee options by creating skeleton of future tx.
*/
reCalcTx = async (all = false) => {
reCalcTx = (all = false) => {
const wallet = this.state.fromWallet;
const fees = this.state.networkTransactionFees;
const changeAddress = await wallet.getChangeAddressAsync();
const changeAddress = this.getChangeAddressFast();
const requestedSatPerByte = Number(this.state.fee);
const feePrecalc = { ...this.state.feePrecalc };
@ -547,7 +604,7 @@ export default class SendDetails extends Component {
async createPsbtTransaction() {
/** @type {HDSegwitBech32Wallet} */
const wallet = this.state.fromWallet;
const changeAddress = await wallet.getChangeAddressAsync();
const changeAddress = await this.getChangeAddressAsync();
const requestedSatPerByte = Number(this.state.fee);
console.log({ requestedSatPerByte, utxo: wallet.getUtxo() });