mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2024-11-19 09:50:15 +01:00
291 lines
8.4 KiB
JavaScript
291 lines
8.4 KiB
JavaScript
import Tor from 'react-native-tor';
|
|
const tor = Tor({
|
|
bootstrapTimeoutMs: 35000,
|
|
numberConcurrentRequests: 1,
|
|
});
|
|
|
|
/**
|
|
* TOR wrapper mimicking Frisbee interface
|
|
*/
|
|
class Torsbee {
|
|
baseURI = '';
|
|
|
|
static _testConn;
|
|
static _resolveReference;
|
|
static _rejectReference;
|
|
|
|
constructor(opts) {
|
|
opts = opts || {};
|
|
this.baseURI = opts.baseURI || this.baseURI;
|
|
}
|
|
|
|
async get(path, options) {
|
|
console.log('TOR: starting...');
|
|
const socksProxy = await tor.startIfNotStarted();
|
|
console.log('TOR: started', await tor.getDaemonStatus(), 'on local port', socksProxy);
|
|
if (path.startsWith('/') && this.baseURI.endsWith('/')) {
|
|
// oy vey, duplicate slashes
|
|
path = path.substr(1);
|
|
}
|
|
|
|
const response = {};
|
|
try {
|
|
const uri = this.baseURI + path;
|
|
console.log('TOR: requesting', uri);
|
|
const torResponse = await tor.get(uri, options?.headers || {}, true);
|
|
response.originalResponse = torResponse;
|
|
|
|
if (options?.headers['Content-Type'] === 'application/json' && torResponse.json) {
|
|
response.body = torResponse.json;
|
|
} else {
|
|
response.body = Buffer.from(torResponse.b64Data, 'base64').toString();
|
|
}
|
|
} catch (error) {
|
|
response.err = error;
|
|
console.warn(error);
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
async post(path, options) {
|
|
console.log('TOR: starting...');
|
|
const socksProxy = await tor.startIfNotStarted();
|
|
console.log('TOR: started', await tor.getDaemonStatus(), 'on local port', socksProxy);
|
|
if (path.startsWith('/') && this.baseURI.endsWith('/')) {
|
|
// oy vey, duplicate slashes
|
|
path = path.substr(1);
|
|
}
|
|
|
|
const uri = this.baseURI + path;
|
|
console.log('TOR: posting to', uri);
|
|
|
|
const response = {};
|
|
try {
|
|
const torResponse = await tor.post(uri, JSON.stringify(options?.body || {}), options?.headers || {}, true);
|
|
response.originalResponse = torResponse;
|
|
|
|
if (options?.headers['Content-Type'] === 'application/json' && torResponse.json) {
|
|
response.body = torResponse.json;
|
|
} else {
|
|
response.body = Buffer.from(torResponse.b64Data, 'base64').toString();
|
|
}
|
|
} catch (error) {
|
|
response.err = error;
|
|
console.warn(error);
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
testSocket() {
|
|
return new Promise((resolve, reject) => {
|
|
this.constructor._resolveReference = resolve;
|
|
this.constructor._rejectReference = reject;
|
|
(async () => {
|
|
console.log('testSocket...');
|
|
try {
|
|
if (!this.constructor._testConn) {
|
|
// no test conenctino exists, creating it...
|
|
await tor.startIfNotStarted();
|
|
const target = 'explorerzydxu5ecjrkwceayqybizmpjjznk5izmitf2modhcusuqlid.onion:110';
|
|
this.constructor._testConn = await tor.createTcpConnection({ target }, (data, err) => {
|
|
if (err) {
|
|
return this.constructor._rejectReference(new Error(err));
|
|
}
|
|
const json = JSON.parse(data);
|
|
if (!json || typeof json.result === 'undefined')
|
|
return this.constructor._rejectReference(new Error('Unexpected response from TOR socket: ' + JSON.stringify(json)));
|
|
|
|
// conn.close();
|
|
// instead of closing connect, we will actualy re-cyce existing test connection as we
|
|
// saved it into `this.constructor.testConn`
|
|
this.constructor._resolveReference();
|
|
});
|
|
|
|
await this.constructor._testConn.write(
|
|
`{ "id": 1, "method": "blockchain.scripthash.get_balance", "params": ["716decbe1660861c3d93906cb1d98ee68b154fd4d23aed9783859c1271b52a9c"] }\n`,
|
|
);
|
|
} else {
|
|
// test connectino exists, so we are reusing it
|
|
await this.constructor._testConn.write(
|
|
`{ "id": 1, "method": "blockchain.scripthash.get_balance", "params": ["716decbe1660861c3d93906cb1d98ee68b154fd4d23aed9783859c1271b52a9c"] }\n`,
|
|
);
|
|
}
|
|
} catch (error) {
|
|
this.constructor._rejectReference(error);
|
|
}
|
|
})();
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wrapper for react-native-tor mimicking Socket class from NET package
|
|
*/
|
|
class TorSocket {
|
|
constructor() {
|
|
this._socket = false;
|
|
this._listeners = {};
|
|
}
|
|
|
|
setTimeout() {}
|
|
|
|
setEncoding() {}
|
|
|
|
setKeepAlive() {}
|
|
|
|
setNoDelay() {}
|
|
|
|
on(event, listener) {
|
|
this._listeners[event] = this._listeners[event] || [];
|
|
this._listeners[event].push(listener);
|
|
}
|
|
|
|
removeListener(event, listener) {
|
|
this._listeners[event] = this._listeners[event] || [];
|
|
const newListeners = [];
|
|
|
|
let found = false;
|
|
for (const savedListener of this._listeners[event]) {
|
|
// eslint-disable-next-line eqeqeq
|
|
if (savedListener == listener) {
|
|
// found our listener
|
|
found = true;
|
|
// we just skip it
|
|
} else {
|
|
// other listeners should go back to original array
|
|
newListeners.push(savedListener);
|
|
}
|
|
}
|
|
|
|
if (found) {
|
|
this._listeners[event] = newListeners;
|
|
} else {
|
|
// something went wrong, lets just cleanup all listeners
|
|
this._listeners[event] = [];
|
|
}
|
|
}
|
|
|
|
connect(port, host, callback) {
|
|
console.log('connecting TOR socket...', host, port);
|
|
(async () => {
|
|
console.log('starting tor...');
|
|
try {
|
|
await tor.startIfNotStarted();
|
|
} catch (e) {
|
|
console.warn('Could not bootstrap TOR', e);
|
|
await tor.stopIfRunning();
|
|
this._passOnEvent('error', 'Could not bootstrap TOR');
|
|
return false;
|
|
}
|
|
console.log('started tor');
|
|
const iWillConnectISwear = tor.createTcpConnection({ target: host + ':' + port, connectionTimeout: 15000 }, (data, err) => {
|
|
if (err) {
|
|
console.log('TOR socket onData error: ', err);
|
|
// this._passOnEvent('error', err);
|
|
return;
|
|
}
|
|
this._passOnEvent('data', data);
|
|
});
|
|
|
|
try {
|
|
this._socket = await Promise.race([iWillConnectISwear, new Promise(resolve => setTimeout(resolve, 21000))]);
|
|
} catch (e) {}
|
|
|
|
if (!this._socket) {
|
|
console.log('connecting TOR socket failed'); // either sleep expired or connect threw an exception
|
|
await tor.stopIfRunning();
|
|
this._passOnEvent('error', 'connecting TOR socket failed');
|
|
return false;
|
|
}
|
|
|
|
console.log('TOR socket connected:', host, port);
|
|
setTimeout(() => {
|
|
this._passOnEvent('connect', true);
|
|
callback();
|
|
}, 1000);
|
|
})();
|
|
}
|
|
|
|
_passOnEvent(event, data) {
|
|
this._listeners[event] = this._listeners[event] || [];
|
|
for (const savedListener of this._listeners[event]) {
|
|
savedListener(data);
|
|
}
|
|
}
|
|
|
|
emit(event, data) {}
|
|
|
|
end() {
|
|
console.log('trying to close TOR socket');
|
|
if (this._socket && this._socket.close) {
|
|
console.log('trying to close TOR socket SUCCESS');
|
|
return this._socket.close();
|
|
}
|
|
}
|
|
|
|
destroy() {}
|
|
|
|
write(data) {
|
|
if (this._socket && this._socket.write) {
|
|
try {
|
|
return this._socket.write(data);
|
|
} catch (error) {
|
|
console.log('this._socket.write() failed so we are issuing ERROR event', error);
|
|
this._passOnEvent('error', error);
|
|
}
|
|
} else {
|
|
console.log('TOR socket write error, socket not connected');
|
|
this._passOnEvent('error', 'TOR socket not connected');
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports.getDaemonStatus = async () => {
|
|
try {
|
|
return await tor.getDaemonStatus();
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
module.exports.stopIfRunning = async () => {
|
|
try {
|
|
Torsbee._testConn = false;
|
|
return await tor.stopIfRunning();
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
module.exports.startIfNotStarted = async () => {
|
|
try {
|
|
return await tor.startIfNotStarted();
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
module.exports.testSocket = async () => {
|
|
const c = new Torsbee();
|
|
return c.testSocket();
|
|
};
|
|
|
|
module.exports.testHttp = async () => {
|
|
const api = new Torsbee({
|
|
baseURI: 'http://explorerzydxu5ecjrkwceayqybizmpjjznk5izmitf2modhcusuqlid.onion:80/',
|
|
});
|
|
const torResponse = await api.get('/api/tx/a84dbcf0d2550f673dda9331eea7cab86b645fd6e12049755c4b47bd238adce9', {
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
const json = torResponse.body;
|
|
if (json.txid !== 'a84dbcf0d2550f673dda9331eea7cab86b645fd6e12049755c4b47bd238adce9')
|
|
throw new Error('TOR failure, got ' + JSON.stringify(torResponse));
|
|
};
|
|
|
|
module.exports.Torsbee = Torsbee;
|
|
module.exports.Socket = TorSocket;
|