mirror of
https://github.com/embarklabs/EmbarkJS.git
synced 2025-02-19 23:58:16 +00:00
fix: pass specific connection errors instead of leaking
This commit is contained in:
parent
0262ebe70e
commit
632b3734a0
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
|||||||
dist
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
package
|
package
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
14
package.json
14
package.json
@ -28,7 +28,7 @@
|
|||||||
"clean": "rimraf dist embark.min.js embarkjs-*.tgz package",
|
"clean": "rimraf dist embark.min.js embarkjs-*.tgz package",
|
||||||
"http-server": "http-server",
|
"http-server": "http-server",
|
||||||
"prepare": "npm run build",
|
"prepare": "npm run build",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "mocha --exit",
|
||||||
"webpack": "webpack"
|
"webpack": "webpack"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -56,15 +56,19 @@
|
|||||||
"async-es": "2.6.1"
|
"async-es": "2.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "7.0.0-rc.1",
|
"@babel/cli": "7.2.3",
|
||||||
"@babel/core": "7.0.0-rc.1",
|
"@babel/core": "7.2.2",
|
||||||
"@babel/plugin-transform-runtime": "7.0.0-rc.1",
|
"@babel/plugin-transform-runtime": "7.2.0",
|
||||||
"@babel/preset-env": "7.0.0-rc.1",
|
"@babel/preset-env": "7.2.3",
|
||||||
|
"@babel/register": "7.0.0",
|
||||||
"ajv": "6.5.2",
|
"ajv": "6.5.2",
|
||||||
|
"chai": "4.2.0",
|
||||||
"cross-env": "5.2.0",
|
"cross-env": "5.2.0",
|
||||||
"http-server": "0.11.1",
|
"http-server": "0.11.1",
|
||||||
|
"mocha": "5.2.0",
|
||||||
"npm-run-all": "4.1.5",
|
"npm-run-all": "4.1.5",
|
||||||
"rimraf": "2.6.2",
|
"rimraf": "2.6.2",
|
||||||
|
"web3": "1.0.0-beta.37",
|
||||||
"webpack": "4.16.1",
|
"webpack": "4.16.1",
|
||||||
"webpack-cli": "3.0.8"
|
"webpack-cli": "3.0.8"
|
||||||
},
|
},
|
||||||
|
@ -28,15 +28,23 @@ Blockchain.connect = function(connectionList, opts, doneCb) {
|
|||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
const checkConnect = (next) => {
|
const checkConnect = (next) => {
|
||||||
this.blockchainConnector.getAccounts((err, _a) => {
|
this.blockchainConnector.getAccounts((error, _a) => {
|
||||||
if (err) {
|
const provider = self.blockchainConnector.getCurrentProvider();
|
||||||
this.blockchainConnector.setProvider(null);
|
const connectionString = provider.host;
|
||||||
}
|
|
||||||
return next(null, !err);
|
if (error) this.blockchainConnector.setProvider(null);
|
||||||
|
|
||||||
|
return next(null, {
|
||||||
|
connectionString,
|
||||||
|
error,
|
||||||
|
connected: !error
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const connectWeb3 = async (next) => {
|
const connectWeb3 = async (next) => {
|
||||||
|
const connectionString = 'web3://';
|
||||||
|
|
||||||
if (window.ethereum) {
|
if (window.ethereum) {
|
||||||
try {
|
try {
|
||||||
if (Blockchain.autoEnable) {
|
if (Blockchain.autoEnable) {
|
||||||
@ -45,11 +53,19 @@ Blockchain.connect = function(connectionList, opts, doneCb) {
|
|||||||
}
|
}
|
||||||
return checkConnect(next);
|
return checkConnect(next);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return next(null, false);
|
return next(null, {
|
||||||
|
connectionString,
|
||||||
|
error,
|
||||||
|
connected: false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return next(null, false);
|
return next(null, {
|
||||||
|
connectionString,
|
||||||
|
error: new Error("web3 provider not detected"),
|
||||||
|
connected: false
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const connectWebsocket = (value, next) => {
|
const connectWebsocket = (value, next) => {
|
||||||
@ -62,20 +78,24 @@ Blockchain.connect = function(connectionList, opts, doneCb) {
|
|||||||
checkConnect(next);
|
checkConnect(next);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let connectionErrs = {};
|
||||||
|
|
||||||
this.doFirst(function(cb) {
|
this.doFirst(function(cb) {
|
||||||
reduce(connectionList, false, function(connected, value, next) {
|
reduce(connectionList, false, function(result, connectionString, next) {
|
||||||
if (connected) {
|
if (result.connected) {
|
||||||
return next(null, connected);
|
return next(null, result);
|
||||||
|
} else if(result) {
|
||||||
|
connectionErrs[result.connectionString] = result.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value === '$WEB3') {
|
if (connectionString === '$WEB3') {
|
||||||
connectWeb3(next);
|
connectWeb3(next);
|
||||||
} else if (value.indexOf('ws://') >= 0) {
|
} else if (connectionString.indexOf('ws://') >= 0) {
|
||||||
connectWebsocket(value, next);
|
connectWebsocket(connectionString, next);
|
||||||
} else {
|
} else {
|
||||||
connectHttp(value, next);
|
connectHttp(connectionString, next);
|
||||||
}
|
}
|
||||||
}, function(_err, _connected) {
|
}, function(_err, _connectionErr, _connected) {
|
||||||
self.blockchainConnector.getAccounts((err, accounts) => {
|
self.blockchainConnector.getAccounts((err, accounts) => {
|
||||||
const currentProv = self.blockchainConnector.getCurrentProvider();
|
const currentProv = self.blockchainConnector.getCurrentProvider();
|
||||||
if (opts.warnAboutMetamask && currentProv && currentProv.isMetaMask) {
|
if (opts.warnAboutMetamask && currentProv && currentProv.isMetaMask) {
|
||||||
@ -83,15 +103,18 @@ Blockchain.connect = function(connectionList, opts, doneCb) {
|
|||||||
// embark will only do this if geth is our client and we are in
|
// embark will only do this if geth is our client and we are in
|
||||||
// dev mode
|
// dev mode
|
||||||
if(opts.blockchainClient === 'geth') {
|
if(opts.blockchainClient === 'geth') {
|
||||||
console.warn("%cNote: There is a known issue with Geth that may cause transactions to get stuck when using Metamask. Please log in to the cockpit (http://localhost:8000/embark?enableRegularTxs=true) to enable a workaround. Once logged in, the workaround will automatically be enabled.", "font-size: 2em");
|
console.warn("%cNote: There is a known issue with Geth that may cause transactions to get stuck when using Metamask. Please log in to the cockpit (http://localhost:8000/embark?enableRegularTxs=true) to enable a workaround. Once logged in, the workaround will automatically be enabled.", "font-size: 2em");
|
||||||
}
|
}
|
||||||
console.warn("%cNote: Embark has detected you are in the development environment and using Metamask, please make sure Metamask is connected to your local node", "font-size: 2em");
|
console.warn("%cNote: Embark has detected you are in the development environment and using Metamask, please make sure Metamask is connected to your local node", "font-size: 2em");
|
||||||
}
|
}
|
||||||
if (accounts) {
|
if (accounts) {
|
||||||
self.blockchainConnector.setDefaultAccount(accounts[0]);
|
self.blockchainConnector.setDefaultAccount(accounts[0]);
|
||||||
}
|
}
|
||||||
cb(err);
|
|
||||||
doneCb(err);
|
const connectionError = new BlockchainConnectionError(connectionErrs);
|
||||||
|
|
||||||
|
cb(connectionErrs);
|
||||||
|
doneCb(connectionErrs);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -263,4 +286,13 @@ Contract.prototype.send = function(value, unit, _options) {
|
|||||||
|
|
||||||
Blockchain.Contract = Contract;
|
Blockchain.Contract = Contract;
|
||||||
|
|
||||||
|
class BlockchainConnectionError extends Error {
|
||||||
|
constructor(connectionErrors) {
|
||||||
|
super("Could not establish a connection to a node.");
|
||||||
|
|
||||||
|
this.connections = connectionErrors;
|
||||||
|
this.name = 'BlockchainConnectionError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default Blockchain;
|
export default Blockchain;
|
||||||
|
58
test/blockchain_test.js
Normal file
58
test/blockchain_test.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/* global before,describe,it */
|
||||||
|
const {startRPCMockServer, TestProvider} = require('./test');
|
||||||
|
|
||||||
|
const async = require('async');
|
||||||
|
const {assert} = require('chai');
|
||||||
|
|
||||||
|
const Blockchain = require('../dist/blockchain');
|
||||||
|
|
||||||
|
describe('Blockchain', () => {
|
||||||
|
describe('#connect', () => {
|
||||||
|
before(() => {
|
||||||
|
Blockchain.default.registerProvider('web3', TestProvider);
|
||||||
|
Blockchain.default.setProvider('web3', {});
|
||||||
|
});
|
||||||
|
|
||||||
|
const scenarios = [
|
||||||
|
{
|
||||||
|
description: 'should not keep trying other connections if connected',
|
||||||
|
servers: [true, true],
|
||||||
|
visited: [true, false],
|
||||||
|
error: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'should keep trying other connections if not connected',
|
||||||
|
servers: [false, true],
|
||||||
|
visited: [true, true],
|
||||||
|
error: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'should return error if no connections succeed',
|
||||||
|
servers: [false, false],
|
||||||
|
visited: [true, true],
|
||||||
|
error: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
scenarios.forEach(scenario => {
|
||||||
|
it(scenario.description, done => {
|
||||||
|
async.parallel(
|
||||||
|
scenario.servers.map(validServer =>
|
||||||
|
(cb) => startRPCMockServer({successful: validServer}, cb)
|
||||||
|
),
|
||||||
|
(_err, servers) => {
|
||||||
|
const connStrings = servers.map(server => server.connectionString);
|
||||||
|
Blockchain.default.connect(connStrings, {}, err => {
|
||||||
|
if(scenario.error) assert(err);
|
||||||
|
|
||||||
|
servers.forEach((server, idx) => {
|
||||||
|
assert.strictEqual(server.visited, scenario.visited[idx]);
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
111
test/test.js
Normal file
111
test/test.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
const async = require('async');
|
||||||
|
const http = require('http');
|
||||||
|
const net = require('net');
|
||||||
|
|
||||||
|
const Web3 = require('web3');
|
||||||
|
|
||||||
|
const startRPCMockServer = (options = {}, callback) => {
|
||||||
|
const opts = Object.assign({}, {
|
||||||
|
successful: true
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
let port = 0;
|
||||||
|
let sock = net.createServer();
|
||||||
|
let state = { visited: false };
|
||||||
|
let server = http.createServer({}, (req, res) => {
|
||||||
|
state.visited = true;
|
||||||
|
if(!opts.successful) {
|
||||||
|
res.statusCode = 500;
|
||||||
|
return res.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = '';
|
||||||
|
req.on('data', chunk => {
|
||||||
|
body += chunk.toString();
|
||||||
|
});
|
||||||
|
req.on('end', () => {
|
||||||
|
const request = JSON.parse(body);
|
||||||
|
const accountsResponse = JSON.stringify({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": request.id,
|
||||||
|
"result": [
|
||||||
|
"0x7c67d951b7338a96168f259a16b7ba25e7a30315"
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
res.writeHead(200, {
|
||||||
|
'Content-Length': Buffer.byteLength(accountsResponse),
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
})
|
||||||
|
res.end(accountsResponse);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
cb => { sock.listen(0, cb) },
|
||||||
|
cb => { port = sock.address().port; cb() },
|
||||||
|
cb => { sock.close(cb) },
|
||||||
|
cb => { server.listen(port, '127.0.0.1', () => cb()); }
|
||||||
|
], () => {
|
||||||
|
state.server = server;
|
||||||
|
state.connectionString = `http://localhost:${port}`;
|
||||||
|
callback(null, state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const TestProvider = {};
|
||||||
|
|
||||||
|
TestProvider.init = function(_config) {
|
||||||
|
this.web3 = global.web3 || new Web3();
|
||||||
|
global.web3 = global.web3 || this.web3;
|
||||||
|
};
|
||||||
|
|
||||||
|
TestProvider.getInstance = function () {
|
||||||
|
return this.web3;
|
||||||
|
};
|
||||||
|
|
||||||
|
TestProvider.getAccounts = function () {
|
||||||
|
return this.web3.eth.getAccounts(...arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
TestProvider.getNewProvider = function (providerName, ...args) {
|
||||||
|
return new Web3.providers[providerName](...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
TestProvider.setProvider = function (provider) {
|
||||||
|
return this.web3.setProvider(provider);
|
||||||
|
};
|
||||||
|
|
||||||
|
TestProvider.getCurrentProvider = function () {
|
||||||
|
return this.web3.currentProvider;
|
||||||
|
};
|
||||||
|
|
||||||
|
TestProvider.getDefaultAccount = function () {
|
||||||
|
return this.web3.eth.defaultAccount;
|
||||||
|
};
|
||||||
|
|
||||||
|
TestProvider.setDefaultAccount = function (account) {
|
||||||
|
this.web3.eth.defaultAccount = account;
|
||||||
|
};
|
||||||
|
|
||||||
|
TestProvider.newContract = function (options) {
|
||||||
|
return new this.web3.eth.Contract(options.abi, options.address);
|
||||||
|
};
|
||||||
|
|
||||||
|
TestProvider.send = function () {
|
||||||
|
return this.web3.eth.sendTransaction(...arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
TestProvider.toWei = function () {
|
||||||
|
return this.web3.toWei(...arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
TestProvider.getNetworkId = function () {
|
||||||
|
return this.web3.eth.net.getId();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
TestProvider,
|
||||||
|
|
||||||
|
startRPCMockServer
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user