Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node http endpoints and handlers #168

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ed1b563
Implement sendopen http interface for node. Requires updated hs-clien…
wi-ski May 4, 2019
8a9fe95
Implement grindname, getproof, and getresource
wi-ski May 5, 2019
3fcd483
Use paired hs-client
wi-ski May 5, 2019
ad782fe
Undo need for update hs-client
wi-ski May 14, 2019
1d20d2a
Remaining getInfo calls
wi-ski May 14, 2019
b9b9396
this correction
wi-ski May 14, 2019
0f98759
Update test/node-http-test.js
wi-ski May 14, 2019
5db3f58
Update test/node-http-test.js
wi-ski May 14, 2019
14a5c31
Surface level test tweaks, next try sleep again
wi-ski May 15, 2019
dc5e518
Test implementation using sleep attempt
wi-ski May 15, 2019
0722801
Kill before each
wi-ski May 15, 2019
ffb2fc1
Extend timeout?
wi-ski May 15, 2019
0fdce37
Extend timeout again.
wi-ski May 15, 2019
6baaa6d
Event listening
wi-ski May 15, 2019
b035103
First pass at mining fewer blocks
wi-ski May 15, 2019
721f8fd
Remove sleep
wi-ski May 15, 2019
593a151
Mine even fewer blocks
wi-ski May 15, 2019
5fd20d5
Bring test timeout back down to reflect time seen in CI
wi-ski May 15, 2019
bccab33
PR tweaks for Tynes. Use getJSON util method, variable renaming
wi-ski Jun 7, 2019
32d8557
Try CI again
wi-ski Jun 8, 2019
c0bec63
Fix missing ref
wi-ski Jun 27, 2019
9050228
Fix for var shadowing
wi-ski Jun 27, 2019
3b8a8a4
Update routes, use more determnistic mining setup, implement and test…
wi-ski Jun 30, 2019
4dbf7f7
Update test descriptions for TYPE_DEADEND
wi-ski Jun 30, 2019
41f5088
Tighten test line spacing up
wi-ski Jun 30, 2019
d4015a5
Lint fix
wi-ski Jul 1, 2019
4c6afb4
Return null when DB doesnt know about name
wi-ski Jul 1, 2019
231e30e
Restore empty line
wi-ski Jul 1, 2019
177a78e
Dedup proof response vals, shorten line length
wi-ski Jul 24, 2019
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
129 changes: 129 additions & 0 deletions lib/node/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const Claim = require('../primitives/claim');
const Address = require('../primitives/address');
const Network = require('../protocol/network');
const pkg = require('../pkg');
const rules = require('../covenants/rules');
const Resource = require('../dns/resource');

/**
* HTTP
Expand Down Expand Up @@ -432,6 +434,133 @@ class HTTP extends Server {

res.json(200, { success: true });
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each of these endpoints could use a comment describing their functionality

this.get('/name/:name', async (req, res) => {
const valid = Validator.fromRequest(req);
const name = valid.str('name');

if (!name || !rules.verifyName(name))
throw new Error('Invalid parameter.');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: 'Invalid name.'


const network = this.network;
const height = this.chain.height;
const nameHash = rules.hashName(name);
const reserved = rules.isReserved(nameHash, height + 1, network);
const [start, week] = rules.getRollout(nameHash, network);
const ns = await this.chain.db.getNameState(nameHash);

let info = null;

if (ns) {
if (!ns.isExpired(height, network))
info = ns.getJSON(height, network);
}

return res.json(200, {
start: {
reserved: reserved,
week: week,
start: start
},
info
});
});

this.get('/resource/hash/:hash', async (req, res) => {
const valid = Validator.fromRequest(req);
const hash = valid.bhash('hash');

if (!hash)
throw new Error('Invalid hash.');

const ns = await this.chain.db.getNameState(hash);
const height = this.chain.tip.height;
const network = this.network;

if (!ns)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also need to check if ns.data.length === 0

return res.json(404);

return res.json(200, ns.getJSON(height,network));
});

this.get('/resource/name/:name', async (req, res) => {
const valid = Validator.fromRequest(req);
const name = valid.str('name');

if (!name || !rules.verifyName(name))
throw new Error('Invalid name.');

const nameHash = rules.hashName(name);
const ns = await this.chain.db.getNameState(nameHash);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want to return null here, we want to return a res.json(404)

if (!ns || ns.data.length === 0)
return null;

const resource = Resource.decode(ns.data);

return res.json(200, resource.getJSON(name));
});

this.get('/proof/name/:name', async (req, res) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I think about it, there are different kinds of proofs. There is an Urkle Tree Proof, which authenticates the namestate and then there is a Merkle Proof for transactions in a block. Ideally we want to be able to support both of them and if we use /proof/name/:name then we can use /proof/tx/:txid, but I think that would only work if transaction indexing is turned on. So using /proof/name/:name works.

const valid = Validator.fromRequest(req);
const name = valid.str('name');

if (!name || !rules.verifyName(name))
throw new Error('Invalid name.');

const tip = this.chain.tip.hash;
const height = this.chain.tip.height;
const root = this.chain.tip.treeRoot;
const nameHash = rules.hashName(name);
const proof = await this.chain.db.prove(root, nameHash);

return res.json(200, {
hash: tip.toString('hex'),
height: height,
root: root.toString('hex'),
name: name,
key: nameHash.toString('hex'),
...proof.toJSON()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love this solution, I think it would be more ideal to pass some data into proof.toJSON but then that would couple the proof to a particular blockchain implementation when it could be agnostic. The proof is defined in the urkel library, not hsd.

Maybe something like:

{
  proof: proof.toJSON(),
  chain: {
    height: height
    root: root.toString('hex')
    name: name
  }

We want to be able to support passing in an arbitrary height (create proofs at a certain height) eventually, that feels like it could be its own endpoint GET /proof/name/:name/height/:height or by adding a query param GET /proof/name/:name?height=height

Pinging @boymanjor @pinheadmz for opinions

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to opinionate too much since I'm new to this thread but... To compare, rpc gettxoutproof just returns a raw hex string, and has a matching verifier rpc verifytxoutproof. I think getting a proof is useless (?) without a matching endpoint to verify it, and that should perhaps dictate the output format of the get proof endpoint?

Briefly checking this:

https://github.com/handshake-org/urkel/blob/c29466104b25f85ec2ef7a5025af625a6df21eda/lib/radix/proof.js#L256

verify(root, key, hash, bits) {...

... I would expect a proof-getter to return the proof (either JSON or serialized hex) along with the current (by default) tree root and perhaps the key, which is just the name hash, but the user might not have the hash if they are using this endpoint.

});
});

this.get('/proof/hash/:nameHash', async (req, res) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: nameHash -> hash

const valid = Validator.fromRequest(req);
const nameHash = valid.bhash('nameHash');

if (!nameHash)
throw new Error('Invalid hash.');

const tip = this.chain.tip.hash;
const height = this.chain.tip.height;
const root = this.chain.tip.treeRoot;
const proof = await this.chain.db.prove(root, nameHash);
const ns = await this.chain.db.getNameState(nameHash);

return res.json(200, {
hash: tip.toString('hex'),
height: height,
root: root.toString('hex'),
key: nameHash.toString('hex'),
name: ns ? ns.name.toString() : null,
...proof.toJSON()
});
});

this.get('/grind', async (req, res) => {
const valid = Validator.fromRequest(req);
const size = valid.u32('size');

if (size < 1 || size > 63)
throw new Error('Invalid length.');

const network = this.network;
const height = this.chain.height;

const name = await rules.grindName(size, height + 1, network);

res.json(200, { name });
});
}

/**
Expand Down
Loading