feat(@embarkjs/ens): Introduce dappConnection configuration for namesystem

This commit removes the need for `EmbarkJS.onReady()` and `EmbarkJS.Blockchain.blockchainConnector` APIs
in the ENS provider implementation and instead relies purely on vanilla `Web3`. This comes
with the effect that `EmbarkJS.Names` needs to figure out itself what to connect
to, as well as when a connection has been established.

To make that possible, `EmbarkJS.Names` now implements a similar algorithm to
`EmbarkJS.Blockchain` that tries to connect different endpoint, given a `dappConnection`
configuration.

If no `dappConnection` configuration is given via `namesystem.json`, Embark will fallback
to the same connection list that's provided in `contracts.js|json`.

wip
This commit is contained in:
Pascal Precht 2019-12-03 12:59:47 +01:00 committed by Pascal Precht
parent 37ae942b99
commit 2ae46640e9
2 changed files with 140 additions and 82 deletions

View File

@ -1,8 +1,10 @@
/* global global require */ /* global global require */
import { reduce } from 'async';
let EmbarkJS = global.EmbarkJS || require('embarkjs'); let EmbarkJS = global.EmbarkJS || require('embarkjs');
EmbarkJS = EmbarkJS.default || EmbarkJS; EmbarkJS = EmbarkJS.default || EmbarkJS;
const ENSFunctions = require('./ENSFunctions').default; const ENSFunctions = require('./ENSFunctions').default;
const Web3 = require('web3'); const Web3 = require('web3');
const { RequestManager } = require('web3-core-requestmanager');
const namehash = require('eth-ens-namehash'); const namehash = require('eth-ens-namehash');
const __embarkENS = {}; const __embarkENS = {};
@ -154,6 +156,53 @@ __embarkENS.registryAddresses = {
"4": "0xe7410170f87102DF0055eB195163A03B7F2Bff4A" "4": "0xe7410170f87102DF0055eB195163A03B7F2Bff4A"
}; };
function setProvider(web3, kind, endpoint) {
web3.setProvider(new Web3.providers[kind](endpoint));
}
function connectWebSocket(web3, endpoint, callback) {
setProvider(web3, 'WebsocketProvider', endpoint);
checkConnection(web3, callback);
}
function connectHttp(web3, endpoint, callback) {
setProvider(web3, 'HttpProvider', endpoint);
checkConnection(web3, callback);
}
async function connectWeb3(web3, callback) {
if (typeof window !== 'undefined' && window.ethereum) {
try {
await ethereum.enable();
web3.setProvider(ethereum);
return checkConnect(callback);
} catch (e) {
return callback(null, {
error: e,
connected: false
});
}
}
callback(null, {
connected: false,
error: new Error("web3 provider not detected")
});
}
function checkConnection(web3, callback) {
web3.eth.getAccounts(error => {
if (error) {
web3.setProvider(null);
}
callback(null, {
connected: !error,
error
});
});
}
__embarkENS.web3 = new Web3();
__embarkENS.setProvider = function(config) { __embarkENS.setProvider = function(config) {
const self = this; const self = this;
const ERROR_MESSAGE = 'ENS is not available in this chain'; const ERROR_MESSAGE = 'ENS is not available in this chain';
@ -161,40 +210,42 @@ __embarkENS.setProvider = function(config) {
self.env = config.env; self.env = config.env;
self.ready = false; self.ready = false;
// FIXME EmbarkJS.onReady doesn't work. Possibility of a race condition let connectionErrors = {};
EmbarkJS.onReady(err => {
if (err) { reduce(config.dappConnection, false, (result, connectionString, next) => {
throw new Error(err); if (result.connected) {
return next(null, result);
} }
EmbarkJS.Blockchain.blockchainConnector.getNetworkId()
.then((id) => { if (connectionString === '$WEB3') {
connectWeb3(self.web3, next);
} else if ((/^wss?:\/\//).test(connectionString)) {
connectWebSocket(self.web3, connectionString, next);
} else {
connectHttp(self.web3, connectionString, next);
}
}, async (err, result) => {
if (!result.connected || result.error) {
console.error(result.error);
}
const accounts = await self.web3.eth.getAccounts();
self.web3.eth.defaultAccount = accounts[0];
try {
const id = await self.web3.eth.net.getId()
const registryAddress = self.registryAddresses[id] || config.registryAddress; const registryAddress = self.registryAddresses[id] || config.registryAddress;
self._isAvailable = true; self._isAvailable = true;
self.ens = new EmbarkJS.Blockchain.Contract({ self.ens = new self.web3.eth.Contract(config.registryAbi, registryAddress);
abi: config.registryAbi, self.registrar = new self.web3.eth.Contract(config.registrarAbi, config.registrarAddress);
address: registryAddress, self.resolver = new self.web3.eth.Contract(config.resolverAbi, config.resolverAddress);
web3: EmbarkJS.Blockchain.blockchainConnector.getInstance()
});
self.registrar = new EmbarkJS.Blockchain.Contract({
abi: config.registrarAbi,
address: config.registrarAddress,
web3: EmbarkJS.Blockchain.blockchainConnector.getInstance()
});
self.resolver = new EmbarkJS.Blockchain.Contract({
abi: config.resolverAbi,
address: config.resolverAddress,
web3: EmbarkJS.Blockchain.blockchainConnector.getInstance()
});
self.ready = true; self.ready = true;
}) } catch (err) {
.catch(err => {
self.ready = true; self.ready = true;
if (err.message.indexOf('Provider not set or invalid') > -1) { if (err.message.indexOf('Provider not set or invalid') > -1) {
console.warn(ERROR_MESSAGE); console.warn(ERROR_MESSAGE);
return; return;
} }
console.error(err); console.error(err);
}); };
}); });
}; };
@ -220,7 +271,7 @@ __embarkENS.waitForProviderReady = function() {
__embarkENS.resolve = function (name, callback) { __embarkENS.resolve = function (name, callback) {
const resolve = async (name) => { const resolve = async (name) => {
await this.waitForProviderReady(); await this.waitForProviderReady();
if (!EmbarkJS.Blockchain.blockchainConnector.getDefaultAccount()) { if (!this.web3.eth.defaultAccount) {
throw new Error(defaultAccountNotSetError); throw new Error(defaultAccountNotSetError);
} }
@ -231,11 +282,7 @@ __embarkENS.resolve = function (name, callback) {
if (resolvedAddress === voidAddress) { if (resolvedAddress === voidAddress) {
throw new Error('Name not yet registered'); throw new Error('Name not yet registered');
} }
const resolverContract = new EmbarkJS.Blockchain.Contract({ const resolverContract = new this.web3.eth.Contract(this.resolverInterface, resolvedAddress);
abi: this.resolverInterface,
address: resolvedAddress,
web3: EmbarkJS.Blockchain.blockchainConnector.getInstance()
});
return await resolverContract.methods.addr(node).call(); return await resolverContract.methods.addr(node).call();
} catch (err) { } catch (err) {
const msg = err.message; const msg = err.message;
@ -258,7 +305,7 @@ __embarkENS.resolve = function (name, callback) {
__embarkENS.lookup = function (address, callback) { __embarkENS.lookup = function (address, callback) {
const lookup = async (address) => { const lookup = async (address) => {
await this.waitForProviderReady(); await this.waitForProviderReady();
if (!EmbarkJS.Blockchain.blockchainConnector.getDefaultAccount()) { if (!this.web3.eth.defaultAccount) {
throw new Error(defaultAccountNotSetError); throw new Error(defaultAccountNotSetError);
} }
if (address.startsWith("0x")) { if (address.startsWith("0x")) {
@ -272,11 +319,7 @@ __embarkENS.lookup = function (address, callback) {
if (resolverAddress === voidAddress) { if (resolverAddress === voidAddress) {
throw new Error('Address not associated to a resolver'); throw new Error('Address not associated to a resolver');
} }
const resolverContract = new EmbarkJS.Blockchain.Contract({ const resolverContract = new this.web3.eth.Contract(this.resolverInterface, resolverAddress);
abi: this.resolverInterface,
address: resolverAddress,
web3: EmbarkJS.Blockchain.blockchainConnector.getInstance()
});
return await resolverContract.methods.name(node).call(); return await resolverContract.methods.name(node).call();
} catch (err) { } catch (err) {
const msg = err.message; const msg = err.message;
@ -299,7 +342,7 @@ __embarkENS.lookup = function (address, callback) {
__embarkENS.registerSubDomain = function (name, address, callback) { __embarkENS.registerSubDomain = function (name, address, callback) {
callback = callback || function () {}; callback = callback || function () {};
if (!EmbarkJS.Blockchain.blockchainConnector.getDefaultAccount()) { if (!this.web3.eth.defaultAccount) {
return callback(defaultAccountNotSetError); return callback(defaultAccountNotSetError);
} }
@ -315,11 +358,11 @@ __embarkENS.registerSubDomain = function (name, address, callback) {
// Register function generated by the index // Register function generated by the index
ENSFunctions.registerSubDomain( ENSFunctions.registerSubDomain(
EmbarkJS.Blockchain.blockchainConnector.getInstance(), this.web3,
this.ens, this.ens,
this.registrar, this.registrar,
this.resolver, this.resolver,
EmbarkJS.Blockchain.blockchainConnector.getDefaultAccount(), this.web3.eth.defaultAccount,
name, name,
this.registration.rootDomain, this.registration.rootDomain,
namehash.hash(address.toLowerCase().substr(2) + reverseAddrSuffix), namehash.hash(address.toLowerCase().substr(2) + reverseAddrSuffix),

View File

@ -62,6 +62,32 @@ class ENS {
})(); })();
} }
getEnsConfig(done) {
async.map(
this.config.namesystemConfig.dappConnection || this.config.contractsConfig.dappConnection,
(conn, next) => {
if (conn === '$EMBARK') {
return this.events.request('proxy:endpoint:get', next);
}
next(null, conn);
}, (err, connections) => {
if (err) {
return done(err);
}
done(null, {
env: this.env,
registration: this.config.namesystemConfig.register,
registryAbi: this.ensConfig.ENSRegistry.abiDefinition,
registryAddress: this.ensConfig.ENSRegistry.deployedAddress,
registrarAbi: this.ensConfig.FIFSRegistrar.abiDefinition,
registrarAddress: this.ensConfig.FIFSRegistrar.deployedAddress,
resolverAbi: this.ensConfig.Resolver.abiDefinition,
resolverAddress: this.ensConfig.Resolver.deployedAddress,
dappConnection: connections
});
});
}
async init(cb = () => {}) { async init(cb = () => {}) {
if (this.initated || this.config.namesystemConfig === {} || if (this.initated || this.config.namesystemConfig === {} ||
this.config.namesystemConfig.enabled !== true || this.config.namesystemConfig.enabled !== true ||
@ -90,19 +116,6 @@ class ENS {
this.embark.registerActionForEvent("pipeline:generateAll:before", this.addArtifactFile.bind(this)); this.embark.registerActionForEvent("pipeline:generateAll:before", this.addArtifactFile.bind(this));
} }
getEnsConfig() {
return {
env: this.env,
registration: this.config.namesystemConfig.register,
registryAbi: this.ensConfig.ENSRegistry.abiDefinition,
registryAddress: this.ensConfig.ENSRegistry.deployedAddress,
registrarAbi: this.ensConfig.FIFSRegistrar.abiDefinition,
registrarAddress: this.ensConfig.FIFSRegistrar.deployedAddress,
resolverAbi: this.ensConfig.Resolver.abiDefinition,
resolverAddress: this.ensConfig.Resolver.deployedAddress
};
}
executeCommand(command, args, cb) { executeCommand(command, args, cb) {
switch (command) { switch (command) {
case 'resolve': this.ensResolve(args[0], cb); break; case 'resolve': this.ensResolve(args[0], cb); break;
@ -118,18 +131,19 @@ class ENS {
} }
} }
addArtifactFile(_params, cb) { async addArtifactFile(_params, cb) {
const config = this.getEnsConfig(); this.getEnsConfig((err, config) => {
this.events.request("pipeline:register", { this.events.request("pipeline:register", {
path: [this.config.embarkConfig.generationDir, 'config'], path: [this.config.embarkConfig.generationDir, 'config'],
file: 'namesystem.json', file: 'namesystem.json',
format: 'json', format: 'json',
content: Object.assign({}, this.embark.config.namesystemConfig, config) content: Object.assign({}, this.embark.config.namesystemConfig, config)
}, cb); }, cb);
});
} }
async setProviderAndRegisterDomains(cb = (() => {})) { async setProviderAndRegisterDomains(cb = (() => {})) {
const config = this.getEnsConfig(); this.getEnsConfig(async (err, config) => {
if (this.doSetENSProvider) { if (this.doSetENSProvider) {
this.setupEmbarkJS(config); this.setupEmbarkJS(config);
} }
@ -143,6 +157,7 @@ class ENS {
} }
this.registerConfigDomains(config, cb); this.registerConfigDomains(config, cb);
});
} }
async setupEmbarkJS(config) { async setupEmbarkJS(config) {
@ -356,7 +371,7 @@ class ENS {
} }
send(); send();
} }
], (err) => { ], async (err) => {
self.configured = true; self.configured = true;
if (err && err !== NO_REGISTRATION) { if (err && err !== NO_REGISTRATION) {
self.logger.error('Error while deploying ENS contracts'); self.logger.error('Error while deploying ENS contracts');