REF: start transition to Electrum server

This commit is contained in:
Overtorment 2019-01-29 02:27:07 +00:00 committed by Igor Korsakov
parent 95c98db486
commit 240f2c35cb
15 changed files with 359 additions and 23 deletions

View File

@ -7,6 +7,7 @@ let EV = require('./events');
let currency = require('./currency');
let loc = require('./loc');
let A = require('./analytics');
let BlueElectrum = require('./BlueElectrum'); // eslint-disable-line
/** @type {AppStorage} */
let BlueApp = new AppStorage();

105
BlueElectrum.js Normal file
View File

@ -0,0 +1,105 @@
import { AsyncStorage } from 'react-native';
const ElectrumClient = require('electrum-client');
let bitcoin = require('bitcoinjs-lib');
let reverse = require('buffer-reverse');
const defaultPeer = { host: 'electrum.coinucopia.io', ssl: 50002, tcp: 50001, pruning: null, http: null, https: null };
console.log('begin connection:', JSON.stringify(defaultPeer));
let mainClient = new ElectrumClient(defaultPeer.tcp, defaultPeer.host, 'tcp');
(async () => {
try {
await mainClient.connect();
const ver = await mainClient.server_version('2.7.11', '1.2');
console.log('connected to ', ver);
let peers = await mainClient.serverPeers_subscribe();
// console.log('peers', peers);
if (peers && peers.length > 0) {
AsyncStorage.setItem('ELECTRUM_PEERS', JSON.stringify(peers));
}
} catch (e) {
console.log('bad connection:', JSON.stringify(defaultPeer));
throw new Error();
}
})();
/**
*
* @param address {String}
* @returns {Promise<Object>}
*/
async function getBalanceByAddress(address) {
let script = bitcoin.address.toOutputScript(address);
let hash = bitcoin.crypto.sha256(script);
let reversedHash = Buffer.from(reverse(hash));
let balance = await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex'));
balance.addr = address;
return balance;
}
/**
*
* @param address {String}
* @returns {Promise<Array>}
*/
async function getTransactionsByAddress(address) {
let script = bitcoin.address.toOutputScript(address);
let hash = bitcoin.crypto.sha256(script);
let reversedHash = Buffer.from(reverse(hash));
let history = await mainClient.blockchainScripthash_getHistory(reversedHash.toString('hex'));
return history;
}
/**
*
* @param addresses {Array}
* @returns {Promise<{balance: number, unconfirmed_balance: number}>}
*/
async function multiGetBalanceByAddress(addresses) {
let balance = 0;
let unconfirmedBalance = 0;
for (let addr of addresses) {
let b = await getBalanceByAddress(addr);
balance += b.confirmed;
unconfirmedBalance += b.unconfirmed_balance;
}
return { balance, unconfirmed_balance: unconfirmedBalance };
}
module.exports.getBalanceByAddress = getBalanceByAddress;
module.exports.getTransactionsByAddress = getTransactionsByAddress;
module.exports.multiGetBalanceByAddress = multiGetBalanceByAddress;
module.exports.forceDisconnect = () => {
mainClient.keepAlive = () => {}; // dirty hack to make it stop reconnecting
mainClient.reconnect = () => {}; // dirty hack to make it stop reconnecting
mainClient.close();
};
/*
let addr4elect = 'bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej';
let script = bitcoin.address.toOutputScript(addr4elect);
let hash = bitcoin.crypto.sha256(script);
let reversedHash = Buffer.from(hash.reverse());
console.log(addr4elect, ' maps to ', reversedHash.toString('hex'));
console.log(await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex')));
addr4elect = '1BWwXJH3q6PRsizBkSGm2Uw4Sz1urZ5sCj';
script = bitcoin.address.toOutputScript(addr4elect);
hash = bitcoin.crypto.sha256(script);
reversedHash = Buffer.from(hash.reverse());
console.log(addr4elect, ' maps to ', reversedHash.toString('hex'));
console.log(await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex')));
// let peers = await mainClient.serverPeers_subscribe();
// console.log(peers);
mainClient.keepAlive = () => {}; // dirty hack to make it stop reconnecting
mainClient.reconnect = () => {}; // dirty hack to make it stop reconnecting
mainClient.close();
// setTimeout(()=>process.exit(), 3000); */

64
Electrum.test.js Normal file
View File

@ -0,0 +1,64 @@
/* global it, describe, jasmine */
global.net = require('net');
let BlueElectrum = require('./BlueElectrum');
let assert = require('assert');
describe('Electrum', () => {
it('ElectrumClient can connect and query', async () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100 * 1000;
const ElectrumClient = require('electrum-client');
let bitcoin = require('bitcoinjs-lib');
// let bitcoin = require('bitcoinjs-lib');
const peer = { host: 'electrum.coinucopia.io', ssl: 50002, tcp: 50001, pruning: null, http: null, https: null };
console.log('begin connection:', JSON.stringify(peer));
let mainClient = new ElectrumClient(peer.tcp, peer.host, 'tcp');
try {
await mainClient.connect();
const ver = await mainClient.server_version('2.7.11', '1.2');
console.log('connected to ', ver);
} catch (e) {
console.log('bad connection:', JSON.stringify(peer));
throw new Error();
}
let addr4elect = 'bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej';
let script = bitcoin.address.toOutputScript(addr4elect);
let hash = bitcoin.crypto.sha256(script);
let reversedHash = Buffer.from(hash.reverse());
let balance = await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex'));
assert.ok(balance.confirmed > 0);
addr4elect = '3GCvDBAktgQQtsbN6x5DYiQCMmgZ9Yk8BK';
script = bitcoin.address.toOutputScript(addr4elect);
hash = bitcoin.crypto.sha256(script);
reversedHash = Buffer.from(hash.reverse());
balance = await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex'));
assert.ok(balance.confirmed === 51432);
// let peers = await mainClient.serverPeers_subscribe();
// console.log(peers);
mainClient.keepAlive = () => {}; // dirty hack to make it stop reconnecting
mainClient.reconnect = () => {}; // dirty hack to make it stop reconnecting
mainClient.close();
// setTimeout(()=>process.exit(), 3000);
});
it('BlueElectrum works', async function() {
let address = '3GCvDBAktgQQtsbN6x5DYiQCMmgZ9Yk8BK';
let balance = await BlueElectrum.getBalanceByAddress(address);
assert.strictEqual(balance.confirmed, 51432);
assert.strictEqual(balance.unconfirmed, 0);
assert.strictEqual(balance.addr, address);
let txs = await BlueElectrum.getTransactionsByAddress(address);
assert.strictEqual(txs.length, 1);
for (let tx of txs) {
assert.ok(tx.tx_hash);
assert.ok(tx.height);
}
BlueElectrum.forceDisconnect();
});
});

View File

@ -1,8 +1,15 @@
/* global it, jasmine */
/* global it, jasmine, afterAll */
import { SegwitP2SHWallet, SegwitBech32Wallet, HDSegwitP2SHWallet, HDLegacyBreadwalletWallet, HDLegacyP2PKHWallet } from './class';
global.crypto = require('crypto'); // shall be used by tests under nodejs CLI, but not in RN environment
let assert = require('assert');
let bitcoin = require('bitcoinjs-lib');
global.net = require('net'); // needed by Electrum client. For RN it is proviced in shim.js
let BlueElectrum = require('./BlueElectrum'); // so it connects ASAP
afterAll(() => {
// after all tests we close socket so the test suite can actually terminate
return BlueElectrum.forceDisconnect();
});
it('can convert witness to address', () => {
let address = SegwitP2SHWallet.witnessToAddress('035c618df829af694cb99e664ce1b34f80ad2c3b49bcd0d9c0b1836c66b2d25fd8');
@ -148,6 +155,30 @@ it('Segwit HD (BIP49) can fetch UTXO', async function() {
);
});
it('Segwit HD (BIP49) can fetch balance with many used addresses in hierarchy', async function() {
if (!process.env.HD_MNEMONIC_BIP49_MANY_TX) {
console.error('process.env.HD_MNEMONIC_BIP49_MANY_TX not set, skipped');
return;
}
jasmine.DEFAULT_TIMEOUT_INTERVAL = 90 * 1000;
let hd = new HDSegwitP2SHWallet();
hd.setSecret(process.env.HD_MNEMONIC_BIP49_MANY_TX);
assert.ok(hd.validateMnemonic());
let start = +new Date();
await hd.fetchBalance();
let end = +new Date();
const took = (end - start) / 1000;
took > 15 && console.warn('took', took, "sec to fetch huge HD wallet's balance");
assert.strictEqual(hd.getBalance(), 0.00051432);
await hd.fetchUtxo();
assert.ok(hd.utxo.length > 0);
await hd.fetchTransactions();
assert.strictEqual(hd.getTransactions().length, 107);
});
it('can work with malformed mnemonic', () => {
let mnemonic =
'honey risk juice trip orient galaxy win situate shoot anchor bounce remind horse traffic exotic since escape mimic ramp skin judge owner topple erode';

View File

@ -106,6 +106,7 @@ android {
ndk {
abiFilters "armeabi-v7a", "x86"
}
multiDexEnabled true
}
splits {
abi {
@ -137,6 +138,7 @@ android {
}
dependencies {
implementation project(':react-native-tcp')
implementation project(':@remobile_react-native-qrcode-local-image')
implementation project(':react-native-image-picker')
implementation project(':react-native-webview')
@ -155,6 +157,7 @@ dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation "com.facebook.react:react-native:+" // From node_modules
implementation 'com.android.support:multidex:1.0.3'
}
// Run this once to be able to run the application with BUCK

View File

@ -3,6 +3,7 @@ package io.bluewallet.bluewallet;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.peel.react.TcpSocketsModule;
import com.remobile.qrcodeLocalImage.RCTQRCodeLocalImagePackage;
import com.imagepicker.ImagePickerPackage;
import com.reactnativecommunity.webview.RNCWebViewPackage;
@ -55,6 +56,7 @@ public class MainApplication extends Application implements ReactApplication {
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new TcpSocketsModule(),
new RCTQRCodeLocalImagePackage(),
new ImagePickerPackage(),
new RNCWebViewPackage(),

View File

@ -1,4 +1,6 @@
rootProject.name = 'BlueWallet'
include ':react-native-tcp'
project(':react-native-tcp').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-tcp/android')
include ':@remobile_react-native-qrcode-local-image'
project(':@remobile_react-native-qrcode-local-image').projectDir = new File(rootProject.projectDir, '../node_modules/@remobile/react-native-qrcode-local-image/android')
include ':react-native-image-picker'

View File

@ -4,6 +4,7 @@ import { WatchOnlyWallet } from './watch-only-wallet';
const bip39 = require('bip39');
const BigNumber = require('bignumber.js');
const bitcoin = require('bitcoinjs-lib');
const BlueElectrum = require('../BlueElectrum');
export class AbstractHDWallet extends LegacyWallet {
static type = 'abstract';
@ -346,24 +347,69 @@ export class AbstractHDWallet extends LegacyWallet {
async fetchBalance() {
try {
const api = new Frisbee({ baseURI: 'https://www.blockonomics.co' });
let response = await api.post('/api/balance', { body: JSON.stringify({ addr: this.getXpub() }) });
// doing binary search for last used externa address
if (response && response.body && response.body.response) {
this.balance = 0;
this.unconfirmed_balance = 0;
this.usedAddresses = [];
for (let addr of response.body.response) {
this.balance += addr.confirmed;
this.unconfirmed_balance += addr.unconfirmed;
this.usedAddresses.push(addr.addr);
let that = this;
// refactor me
// eslint-disable-next-line
async function binarySearchIterationForInternalAddress(index, maxUsedIndex = 0, minUnusedIndex = 100500100, depth = 0) {
if (depth >= 20) return maxUsedIndex + 1; // fail
let txs = await BlueElectrum.getTransactionsByAddress(that._getInternalAddressByIndex(index));
if (txs.length === 0) {
minUnusedIndex = Math.min(minUnusedIndex, index); // set
index = Math.round((index - maxUsedIndex) / 2 + maxUsedIndex);
} else {
maxUsedIndex = Math.max(maxUsedIndex, index); // set
let txs2 = await BlueElectrum.getTransactionsByAddress(that._getInternalAddressByIndex(index + 1));
if (txs2.length === 0) return index + 1; // thats our next free address
index = Math.round((minUnusedIndex - index) / 2 + index);
}
this.balance = new BigNumber(this.balance).dividedBy(100000000).toString() * 1;
this.unconfirmed_balance = new BigNumber(this.unconfirmed_balance).dividedBy(100000000).toString() * 1;
this._lastBalanceFetch = +new Date();
} else {
throw new Error('Could not fetch balance from API: ' + response.err);
return binarySearchIterationForInternalAddress(index, maxUsedIndex, minUnusedIndex, depth + 1);
}
this.next_free_change_address_index = await binarySearchIterationForInternalAddress(100);
// refactor me
// eslint-disable-next-line
async function binarySearchIterationForExternalAddress(index, maxUsedIndex = 0, minUnusedIndex = 100500100, depth = 0) {
if (depth >= 20) return maxUsedIndex + 1; // fail
let txs = await BlueElectrum.getTransactionsByAddress(that._getExternalAddressByIndex(index));
if (txs.length === 0) {
minUnusedIndex = Math.min(minUnusedIndex, index); // set
index = Math.round((index - maxUsedIndex) / 2 + maxUsedIndex);
} else {
maxUsedIndex = Math.max(maxUsedIndex, index); // set
let txs2 = await BlueElectrum.getTransactionsByAddress(that._getExternalAddressByIndex(index + 1));
if (txs2.length === 0) return index + 1; // thats our next free address
index = Math.round((minUnusedIndex - index) / 2 + index);
}
return binarySearchIterationForExternalAddress(index, maxUsedIndex, minUnusedIndex, depth + 1);
}
this.next_free_address_index = await binarySearchIterationForExternalAddress(100);
this.balance = 0;
this.unconfirmed_balance = 0;
this.usedAddresses = [];
// generating all involved addresses:
for (let c = 0; c < this.next_free_address_index; c++) {
this.usedAddresses.push(this._getExternalAddressByIndex(c));
}
for (let c = 0; c < this.next_free_change_address_index; c++) {
this.usedAddresses.push(this._getInternalAddressByIndex(c));
}
// finally fetching balance
let balance = await BlueElectrum.multiGetBalanceByAddress(this.usedAddresses);
this.balance = new BigNumber(balance.balance).dividedBy(100000000).toNumber();
this.unconfirmed_balance = new BigNumber(balance.unconfirmed_balance).dividedBy(100000000).toNumber();
this._lastBalanceFetch = +new Date();
} catch (err) {
console.warn(err);
}

View File

@ -5,7 +5,6 @@
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
@ -80,6 +79,7 @@
ED2971652150620600B7C4FE /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED2971642150620600B7C4FE /* JavaScriptCore.framework */; };
F21429E1449249038A7F3444 /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 334051161886419EA186F4BA /* FontAwesome.ttf */; };
FBB34FB8F9B248A89346FE61 /* libRNDeviceInfo-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EB3338E347F4AFAA8C85C04 /* libRNDeviceInfo-tvOS.a */; };
34582CAA4AD140F7B80C961A /* libTcpSockets.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DF4E6C040764E4BA1ACC1EB /* libTcpSockets.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -613,6 +613,8 @@
F9065403A26440679749C7AA /* BVLinearGradient.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = BVLinearGradient.xcodeproj; path = "../node_modules/react-native-linear-gradient/BVLinearGradient.xcodeproj"; sourceTree = "<group>"; };
FC98DC24A81A463AB8B2E6B1 /* libRNImagePicker.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNImagePicker.a; sourceTree = "<group>"; };
FD7977067E1A496F94D8B1B7 /* libRNDeviceInfo.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNDeviceInfo.a; sourceTree = "<group>"; };
910283A2A9EB4D00902DE78E /* TcpSockets.xcodeproj */ = {isa = PBXFileReference; name = "TcpSockets.xcodeproj"; path = "../node_modules/react-native-tcp/ios/TcpSockets.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
9DF4E6C040764E4BA1ACC1EB /* libTcpSockets.a */ = {isa = PBXFileReference; name = "libTcpSockets.a"; path = "libTcpSockets.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -661,6 +663,7 @@
02DEC1C9F61B405E8E357B2E /* libRCTWKWebView.a in Frameworks */,
A6E5EEC7A4B54F5A9C9D92FC /* libRNImagePicker.a in Frameworks */,
C1056BF235EE4E23AAF21975 /* libRCTQRCodeLocalImage.a in Frameworks */,
34582CAA4AD140F7B80C961A /* libTcpSockets.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -898,6 +901,7 @@
E7173EC6B95B4981AD3D4C70 /* RCTWKWebView.xcodeproj */,
1A03CFBC35DD4AC28FA4A619 /* RNImagePicker.xcodeproj */,
7EA61BC8FF6E4AD2A67F1557 /* RCTQRCodeLocalImage.xcodeproj */,
910283A2A9EB4D00902DE78E /* TcpSockets.xcodeproj */,
);
name = Libraries;
sourceTree = "<group>";
@ -1964,6 +1968,7 @@
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
);
INFOPLIST_FILE = BlueWalletTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
@ -1992,6 +1997,7 @@
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
@ -2028,6 +2034,7 @@
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
);
INFOPLIST_FILE = BlueWalletTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
@ -2056,6 +2063,7 @@
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
@ -2093,6 +2101,7 @@
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
);
INFOPLIST_FILE = BlueWallet/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
@ -2134,6 +2143,7 @@
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
);
INFOPLIST_FILE = BlueWallet/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
@ -2182,6 +2192,7 @@
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
);
INFOPLIST_FILE = "BlueWallet-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@ -2209,6 +2220,7 @@
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
@ -2254,6 +2266,7 @@
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
);
INFOPLIST_FILE = "BlueWallet-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@ -2281,6 +2294,7 @@
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
@ -2325,6 +2339,7 @@
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
);
INFOPLIST_FILE = "BlueWallet-tvOSTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@ -2352,6 +2367,7 @@
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
@ -2396,6 +2412,7 @@
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
);
INFOPLIST_FILE = "BlueWallet-tvOSTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@ -2423,6 +2440,7 @@
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",

49
package-lock.json generated
View File

@ -3004,8 +3004,7 @@
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"optional": true
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"constants-browserify": {
"version": "1.0.0",
@ -3468,6 +3467,15 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.105.tgz",
"integrity": "sha512-MWZZTThmQR82yzSPOn0BJ2ayZL1l7QyQpS22Frkxn1se9ZJ1WlSwnH8/CeBVhv4IStRxUDBvypeNy2dqsKR6sQ=="
},
"electrum-client": {
"version": "git+https://github.com/Overtorment/node-electrum-client.git#59712b3b7dbe666431eeb5649d6f6541529d386a",
"from": "git+https://github.com/Overtorment/node-electrum-client.git"
},
"electrum-host-parse": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/electrum-host-parse/-/electrum-host-parse-0.1.1.tgz",
"integrity": "sha512-N1ua6Xc5aMcVWPqxlIyiSV/tDGlbGP+S4bIR6KKGDv227VnzXibcin+4t25b5spjDmhfbbk0rIqV1T3MV5WlBQ=="
},
"elliptic": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
@ -9665,7 +9673,6 @@
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -9674,8 +9681,7 @@
"yallist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"optional": true
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
}
}
},
@ -11763,6 +11769,39 @@
"prop-types": "^15.6.1"
}
},
"react-native-tcp": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/react-native-tcp/-/react-native-tcp-3.3.0.tgz",
"integrity": "sha512-WAiL3IGhVxvmM0iw7/5ZRzdkiJBfBbLoFKNaHLJIsTZVqD/N5ZHUlJMjIQ+RrmB/f5nJ82bUEZ2vPCxe+rlNqQ==",
"requires": {
"base64-js": "0.0.8",
"buffer": "^5.0.0",
"events": "^1.0.2",
"ip-regex": "^1.0.3",
"process": "^0.11.9",
"util": "^0.10.3"
},
"dependencies": {
"base64-js": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
"integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg="
},
"ip-regex": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-1.0.3.tgz",
"integrity": "sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0="
},
"util": {
"version": "0.10.4",
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
"integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
"requires": {
"inherits": "2.0.3"
}
}
}
},
"react-native-vector-icons": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-6.2.0.tgz",

View File

@ -45,6 +45,8 @@
"buffer-reverse": "^1.0.1",
"crypto-js": "^3.1.9-1",
"dayjs": "^1.8.0",
"electrum-client": "git+https://github.com/Overtorment/node-electrum-client.git",
"electrum-host-parse": "^0.1.1",
"eslint-config-prettier": "^3.6.0",
"eslint-config-standard": "^12.0.0",
"eslint-config-standard-react": "^7.0.2",
@ -83,6 +85,7 @@
"react-native-snap-carousel": "^3.7.5",
"react-native-sortable-list": "0.0.22",
"react-native-svg": "^9.0.4",
"react-native-tcp": "^3.3.0",
"react-native-vector-icons": "^6.2.0",
"react-native-webview": "^3.2.1",
"react-native-wkwebview-reborn": "^2.0.0",

View File

@ -5,6 +5,8 @@ import PropTypes from 'prop-types';
import { SegwitP2SHWallet, LegacyWallet, HDSegwitP2SHWallet } from '../class';
let BigNumber = require('bignumber.js');
let encryption = require('../encryption');
let bitcoin = require('bitcoinjs-lib');
let BlueElectrum = require('../BlueElectrum');
export default class Selftest extends Component {
static navigationOptions = () => ({
@ -42,6 +44,20 @@ export default class Selftest extends Component {
//
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
let addr4elect = '3GCvDBAktgQQtsbN6x5DYiQCMmgZ9Yk8BK';
let electrumBalance = await BlueElectrum.getBalanceByAddress(addr4elect);
if (electrumBalance.confirmed !== 51432)
throw new Error('BlueElectrum getBalanceByAddress failure, got ' + JSON.stringify(electrumBalance));
let electrumTxs = await BlueElectrum.getTransactionsByAddress(addr4elect);
if (electrumTxs.length !== 1) throw new Error('BlueElectrum getTransactionsByAddress failure, got ' + JSON.stringify(electrumTxs));
} else {
console.warn('skipping RN-specific test');
}
//
let l = new LegacyWallet();
l.setSecret('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct');
if (l.getAddress() !== '19AAjaTUbRjQCMuVczepkoPswiZRhjtg31') {
@ -158,7 +174,6 @@ export default class Selftest extends Component {
];
let tx = l.createTx(utxo, 0.001, 0.0001, '1QHf8Gp3wfmFiSdEX4FtrssCGR68diN1cj');
let bitcoin = require('bitcoinjs-lib');
let txDecoded = bitcoin.Transaction.fromHex(tx);
let txid = txDecoded.getId();

View File

@ -82,7 +82,10 @@ export default class WalletsList extends Component {
// more responsive
let noErr = true;
try {
let balanceStart = +new Date();
await BlueApp.fetchWalletBalances(that.lastSnappedTo || 0);
let balanceEnd = +new Date();
console.log('fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec');
let start = +new Date();
await BlueApp.fetchWalletTransactions(that.lastSnappedTo || 0);
let end = +new Date();

View File

@ -150,9 +150,12 @@ export default class WalletTransactions extends Component {
try {
/** @type {LegacyWallet} */
let wallet = that.state.wallet;
let balanceStart = +new Date();
const oldBalance = wallet.getBalance();
await wallet.fetchBalance();
if (oldBalance !== wallet.getBalance()) smthChanged = true;
let balanceEnd = +new Date();
console.log(wallet.getLabel(), 'fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec');
let start = +new Date();
const oldTxLen = wallet.getTransactions().length;
await wallet.fetchTransactions();

View File

@ -1,4 +1,5 @@
/* global __DEV__, localStorage */
global.net = require('react-native-tcp');
if (typeof __dirname === 'undefined') global.__dirname = '/';
if (typeof __filename === 'undefined') global.__filename = '';
if (typeof process === 'undefined') {