feat(@embark/core): Support directives in ENS config

Support directives in ENS configurations, such that subdomains can be registered to addresses of deployed tokens.

The following directives are supported:
```
"register": {
      "rootDomain": "embark.eth",
      "subdomains": {
        "status": "0x4a17f35f0a9927fb4141aa91cbbc72c1b31598de",
        "mytoken": "$MyToken",
        "MyToken2": "$MyToken2"
      }
    }
```

Add unit test for these directives.
This commit is contained in:
emizzle 2018-12-05 15:41:05 +11:00 committed by Iuri Matias
parent c24536d8a6
commit 75111569a2
3 changed files with 70 additions and 13 deletions

View File

@ -5,7 +5,7 @@ const async = require('async');
const embarkJsUtils = require('embarkjs').Utils; const embarkJsUtils = require('embarkjs').Utils;
const reverseAddrSuffix = '.addr.reverse'; const reverseAddrSuffix = '.addr.reverse';
const ENSFunctions = require('./ENSFunctions'); const ENSFunctions = require('./ENSFunctions');
import { ZERO_ADDRESS } from '../../utils/addressUtils'; import {ZERO_ADDRESS} from '../../utils/addressUtils';
import {ens} from '../../constants'; import {ens} from '../../constants';
const ENS_WHITELIST = ens.whitelist; const ENS_WHITELIST = ens.whitelist;
@ -223,6 +223,7 @@ class ENS {
this.events.request("blockchain:defaultAccount:get", (defaultAccount) => { this.events.request("blockchain:defaultAccount:get", (defaultAccount) => {
async.each(Object.keys(this.registration.subdomains), (subDomainName, eachCb) => { async.each(Object.keys(this.registration.subdomains), (subDomainName, eachCb) => {
const address = this.registration.subdomains[subDomainName]; const address = this.registration.subdomains[subDomainName];
const directivesRegExp = new RegExp(/\$(\w+\[?\d?\]?)/g);
this.ensResolve(`${subDomainName}.${this.registration.rootDomain}`, (error, currentAddress) => { this.ensResolve(`${subDomainName}.${this.registration.rootDomain}`, (error, currentAddress) => {
if (currentAddress && currentAddress.toLowerCase() === address.toLowerCase()) { if (currentAddress && currentAddress.toLowerCase() === address.toLowerCase()) {
return eachCb(); return eachCb();
@ -233,14 +234,30 @@ class ENS {
return eachCb(error); return eachCb(error);
} }
const directives = directivesRegExp.exec(address);
if (directives && directives.length) {
this.embark.registerActionForEvent("contracts:deploy:afterAll", async (deployActionCb) => {
this.events.request("contracts:contract", directives[1], (contract) => {
if(!contract) return deployActionCb(); // if the contract is not registered in the config, it will be undefined here
const reverseNode = utils.soliditySha3(contract.deployedAddress.toLowerCase().substr(2) + reverseAddrSuffix);
this.registerSubDomain(defaultAccount, subDomainName, reverseNode, contract.deployedAddress, secureSend, deployActionCb);
});
});
return eachCb();
}
const reverseNode = utils.soliditySha3(address.toLowerCase().substr(2) + reverseAddrSuffix); const reverseNode = utils.soliditySha3(address.toLowerCase().substr(2) + reverseAddrSuffix);
ENSFunctions.registerSubDomain(this.ensContract, this.registrarContract, this.resolverContract, defaultAccount, this.registerSubDomain(defaultAccount, subDomainName, reverseNode, address, secureSend, eachCb);
subDomainName, this.registration.rootDomain, reverseNode, address, this.logger, secureSend, eachCb);
}); });
}, cb); }, cb);
}); });
} }
registerSubDomain(defaultAccount, subDomainName, reverseNode, address, secureSend, cb) {
ENSFunctions.registerSubDomain(this.ensContract, this.registrarContract, this.resolverContract, defaultAccount,
subDomainName, this.registration.rootDomain, reverseNode, address, this.logger, secureSend, cb);
}
createResolverContract(config, callback) { createResolverContract(config, callback) {
this.events.request("blockchain:contract:create", { this.events.request("blockchain:contract:create", {
abi: config.resolverAbi, abi: config.resolverAbi,
@ -253,7 +270,7 @@ class ENS {
registerAPI() { registerAPI() {
let self = this; let self = this;
const createInternalResolverContract = function(resolverAddress, callback) { const createInternalResolverContract = function (resolverAddress, callback) {
self.createResolverContract({resolverAbi: self.ensConfig.Resolver.abiDefinition, resolverAddress}, callback); self.createResolverContract({resolverAbi: self.ensConfig.Resolver.abiDefinition, resolverAddress}, callback);
}; };
@ -262,10 +279,10 @@ class ENS {
'/embark-api/ens/resolve', '/embark-api/ens/resolve',
(req, res) => { (req, res) => {
async.waterfall([ async.waterfall([
function(callback) { function (callback) {
ENSFunctions.resolveName(req.query.name, self.ensContract, createInternalResolverContract.bind(self), callback); ENSFunctions.resolveName(req.query.name, self.ensContract, createInternalResolverContract.bind(self), callback);
} }
], function(error, address) { ], function (error, address) {
if (error) { if (error) {
return res.send({error: error.message}); return res.send({error: error.message});
} }
@ -279,10 +296,10 @@ class ENS {
'/embark-api/ens/lookup', '/embark-api/ens/lookup',
(req, res) => { (req, res) => {
async.waterfall([ async.waterfall([
function(callback) { function (callback) {
ENSFunctions.lookupAddress(req.query.address, self.ensContract, utils, createInternalResolverContract.bind(self), callback); ENSFunctions.lookupAddress(req.query.address, self.ensContract, utils, createInternalResolverContract.bind(self), callback);
} }
], function(error, name) { ], function (error, name) {
if (error) { if (error) {
return res.send({error: error || error.message}); return res.send({error: error || error.message});
} }
@ -315,10 +332,10 @@ class ENS {
const self = this; const self = this;
// get namehash, import it into file // get namehash, import it into file
self.events.request("version:get:eth-ens-namehash", function(EnsNamehashVersion) { self.events.request("version:get:eth-ens-namehash", function (EnsNamehashVersion) {
let currentEnsNamehashVersion = require('../../../../package.json').dependencies["eth-ens-namehash"]; let currentEnsNamehashVersion = require('../../../../package.json').dependencies["eth-ens-namehash"];
if (EnsNamehashVersion !== currentEnsNamehashVersion) { if (EnsNamehashVersion !== currentEnsNamehashVersion) {
self.events.request("version:getPackageLocation", "eth-ens-namehash", EnsNamehashVersion, function(err, location) { self.events.request("version:getPackageLocation", "eth-ens-namehash", EnsNamehashVersion, function (err, location) {
self.embark.registerImportFile("eth-ens-namehash", fs.dappPath(location)); self.embark.registerImportFile("eth-ens-namehash", fs.dappPath(location));
}); });
} }
@ -475,7 +492,7 @@ class ENS {
if (!self.enabled) { if (!self.enabled) {
return cb('ENS not enabled'); return cb('ENS not enabled');
} }
if(!self.configured) { if (!self.configured) {
return cb('ENS not configured'); return cb('ENS not configured');
} }
const hashedName = namehash.hash(name); const hashedName = namehash.hash(name);
@ -485,7 +502,7 @@ class ENS {
if (err) { if (err) {
return next(err); return next(err);
} }
if(resolverAddress === ZERO_ADDRESS) { if (resolverAddress === ZERO_ADDRESS) {
return next(NOT_REGISTERED_ERROR); return next(NOT_REGISTERED_ERROR);
} }
next(null, resolverAddress); next(null, resolverAddress);

View File

@ -8,7 +8,9 @@
"register": { "register": {
"rootDomain": "embark.eth", "rootDomain": "embark.eth",
"subdomains": { "subdomains": {
"status": "0x4a17f35f0a9927fb4141aa91cbbc72c1b31598de" "status": "0x4a17f35f0a9927fb4141aa91cbbc72c1b31598de",
"mytoken": "$MyToken",
"MyToken2": "$MyToken2"
} }
} }
} }

View File

@ -0,0 +1,38 @@
/*global describe, it, web3, config*/
const assert = require('assert');
const MyToken = require('Embark/contracts/MyToken');
const MyToken2 = require('Embark/contracts/MyToken2');
const EmbarkJS = require('Embark/EmbarkJS');
config({
contracts: {
"Token": {
deploy: false,
args: [1000]
},
"MyToken": {
instanceOf: "Token"
},
"MyToken2": {
instanceOf: "Token",
args: [2000]
}
}
});
describe("ENS functions", function() {
it('should allow directives in ENS subdomains', async function() {
const myTokenAddress = await EmbarkJS.Names.resolve('mytoken.embark.eth');
assert.strictEqual(MyToken.options.address, myTokenAddress);
const myToken2Address = await EmbarkJS.Names.resolve('MyToken2.embark.eth');
assert.strictEqual(MyToken2.options.address, myToken2Address);
const myTokenName = await EmbarkJS.Names.lookup(MyToken.options.address.toLowerCase());
assert.strictEqual(myTokenName, 'mytoken.embark.eth');
const myToken2Name = await EmbarkJS.Names.lookup(MyToken2.options.address.toLowerCase());
assert.strictEqual(myToken2Name, 'MyToken2.embark.eth');
});
});