Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 43 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,22 @@ npm install imperviousinc/handover
```

Your Infura credentials can be passed to the plugin in the same way(s) as all
other `hsd` configuraiton parameters:
other `hsd` configuration parameters:

Command line:

```
hsd \
--plugins=handover \
--handover-provider=infura
--handover-infura-projectid=<...> \
--handover-infura-projectsecret=<...>
```
```

Environment variables:

```
export HSD_HANDOVER_PROVIDER=infura
export HSD_HANDOVER_INFURA_PROJECTID=<...>
export HSD_HANDOVER_INFURA_PROJECTSECRET=<...>
hsd --plugins handover
Expand All @@ -40,6 +42,7 @@ Configuration file:
`~/.hsd/hsd.conf`:

```
handover-provider: infura
handover-infura-projectid: <...>
handover-infura-projectsecret: <...>
plugins: handover
Expand Down Expand Up @@ -98,6 +101,42 @@ $ node
'096365727469666965640662616461737300000100010000ea600004b8495201'
```

## Using a local Ethereum provider

To use a local Etherum node over JSON-RPC instead of Infura, use these configuration options instead.

* `handover-jsonrpc-ens-address` is an ethereum address for the ENS registry to use when resolving '.eth' requests.
* `handover-jsonrpc-url` (optional) specify the jsonrpc connection url. If not provided, will use the ethersjs default.

Command line:

```
hsd \
--plugins=handover \
--handover-provider=jsonrpc \
--handover-jsonrpc-url=<...> \
--handover-jsonrpc-ens-address=<...>
```

Environment variables:

```
export HSD_HANDOVER_INFURA_PROJECTID=<...>
export HSD_HANDOVER_INFURA_PROJECTSECRET=<...>
hsd --plugins handover
```

Configuration file:

`~/.hsd/hsd.conf`:

```
handover-provider: jsonrpc
handover-jsonrpc-url: <...>
handover-jsonrpc-ens-address: <...>
plugins: handover
```

## Explanation

When `hsd` is run with this plugin, a middleware function is added to the HNS root
Expand All @@ -122,8 +161,8 @@ request with the full query string (i.e. `certified.badass`).

There's still a lot "TODO":

Local Ethereum provider: Currently the plugin relies on the Infura API. However,
it is trivial to add an option to use a local Ethereum full node instead.
More config options: Support running against live testnets, without a '.ens' resolver
(to simplify testing), etc.

Cache: The Ethereum interface should cache "resolver" and "registry" contract
objects instead of requesting them from the Ethereum provider on each query.
Expand Down
48 changes: 41 additions & 7 deletions lib/ethereum.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const ethers = require('ethers');
const {encoding, wire} = require('bns');

const ENS_ADDRESS = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e';
const MAINNET_ENS_ADDRESS = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e';
const ENS_ABI = [
'function setOwner(bytes32 node, address owner) external @500000',
'function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external @500000',
Expand All @@ -26,14 +26,48 @@ const RESOLVER_ABI = [
'function hasDNSRecords(bytes32 node, bytes32 name) view returns (bool)'
];

/**
* Ethereum configuration options
*
* @typedef {{
* provider: 'infura'| 'jsonrpc',
* jsonRpcUrl?: string | ethers.ethers.utils.ConnectionInfo,
* network?: ethers.ethers.providers.Networkish,
* localhostEnsAddress?: string,
* projectId?: string,
* projectSecret?: string,
* }} Options
*/

class Ethereum {
/**
* @param {Options} options
*/
constructor(options) {
this.keccak256 = ethers.utils.keccak256;
this.namehash = ethers.utils.namehash;

this.infura = new ethers.providers.InfuraProvider('homestead', options);
/** @type { ethers.providers.Provider & ethers.providers.EnsProvider } */
let provider;
/** @type { string } */
let ensAddress;

switch (options.provider) {
case 'infura':
provider = new ethers.providers.InfuraProvider('homestead', options);
ensAddress = MAINNET_ENS_ADDRESS;
break;
case 'jsonrpc':
provider = new ethers.providers.JsonRpcProvider(options.jsonRpcUrl)
ensAddress = options.localhostEnsAddress;
break;
default:
throw new Error("no provider specified for handover");
}

this.provider = provider;

this.ensRegistry = new ethers.Contract(ENS_ADDRESS, ENS_ABI, this.infura);
this.ensRegistry = new ethers.Contract(ensAddress, ENS_ABI, this.provider);
this.ensResolver = null;
}

Expand All @@ -48,20 +82,20 @@ class Ethereum {
// TODO: cache these
async getResolverFromRegistry(name, registry) {
const resolverAddr = await registry.resolver(this.namehash(name));
return new ethers.Contract(resolverAddr, RESOLVER_ABI, this.infura);
return new ethers.Contract(resolverAddr, RESOLVER_ABI, this.provider);
}

getAbstractEnsRegistry(address) {
return new ethers.Contract(address, ENS_ABI, this.infura);
return new ethers.Contract(address, ENS_ABI, this.provider);
}

async resolveEnsAddress(name) {
return this.infura.resolveName(name);
return this.provider.resolveName(name);
}

// https://eips.ethereum.org/EIPS/eip-634
async resolveEnsText(name, key) {
const nameResolver = await this.infura.getResolver(name);
const nameResolver = await this.provider.getResolver(name);

if (!nameResolver)
return null;
Expand Down
28 changes: 25 additions & 3 deletions lib/handover.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,38 @@ const Ethereum = require('./ethereum');

const plugin = exports;

/**
* @typedef { import("hsd/lib/node").FullNode } FullNode
* @typedef { import("bcfg/lib/config")} Config
*/

class Plugin {
/**
* @param { FullNode } node
*/
constructor(node) {
this.ready = false;
this.node = node;
this.ns = node.ns;
this.logger = node.logger.context('handover');

/** @type { Config } */
const config = node.config;

const provider = config.str('handover-provider');
if (provider !== 'infura' && provider != 'jsonrpc') {
throw new Error(`Configured with invalid provider type: ${provider}`);
}

// For now, just set all the possible options from config, missing ones
// aren't a problem for the unused providers.
this.ethereum = new Ethereum({
projectId: node.config.str('handover-infura-projectid'),
projectSecret: node.config.str('handover-infura-projectsecret')
provider,
projectId: config.str('handover-infura-projectid'),
projectSecret: config.str('handover-infura-projectsecret'),
jsonRpcUrl: config.str('handover-jsonrpc-url'),
localhostEnsAddress: config.str('handover-jsonrpc-ens-address'),
network: config.str('handover-network'),
});

// Plugin can not operate if node doesn't have DNS resolvers
Expand Down Expand Up @@ -97,7 +119,7 @@ class Plugin {
// Look up an alternate (forked) ENS contract by the Ethereum
// address specified in the NS record, and query it for
// the user's original request
if (rr.data.ns.slice(-6) === '._eth.') {
if (rr.data.ns.endsWith('._eth.')) {
hasEnsReferral = true;

// If the recursive is being minimal, don't look up the name.
Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"bns": "~0.14.0"
},
"devDependencies": {
"@types/node": "^14.14.41",
"bmocha": "^2.1.5"
}
}