From ed1b563af8e4aeab926a1d43f0fd2c5cff01e26e Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Sat, 4 May 2019 12:13:22 -0700 Subject: [PATCH 01/29] Implement sendopen http interface for node. Requires updated hs-client lib to work --- lib/node/http.js | 47 +++++++++++++ package.json | 2 +- test/node-http-test.js | 154 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 test/node-http-test.js diff --git a/lib/node/http.js b/lib/node/http.js index 380c47745..6a39bef20 100644 --- a/lib/node/http.js +++ b/lib/node/http.js @@ -21,6 +21,7 @@ const Claim = require('../primitives/claim'); const Address = require('../primitives/address'); const Network = require('../protocol/network'); const pkg = require('../pkg'); +const rules = require('../covenants/rules'); /** * HTTP @@ -432,6 +433,52 @@ class HTTP extends Server { res.json(200, { success: true }); }); + + this.get('/info/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.'); + + 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('/name/hash/:hash', async (req, res) => { + const valid = Validator.fromRequest(req); + const hash = valid.bhash('hash'); + + if (!hash) + throw new Error('Invalid parameter.'); + + const ns = await this.chain.db.getNameState(hash); + + if (!ns) + return res.json(404); + + return res.json(200, { name: ns.name.toString('binary') }); + }); } /** diff --git a/package.json b/package.json index dd7109dad..b39ca9929 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "bval": "~0.1.6", "bweb": "~0.1.8", "goosig": "~0.1.0", - "hs-client": "~0.0.6", + "hs-client": "file:/run/media/wiski/549A5F9E28D6FB4C/hs-client", "mrmr": "~0.1.8", "n64": "~0.2.9", "urkel": "~0.6.3" diff --git a/test/node-http-test.js b/test/node-http-test.js new file mode 100644 index 000000000..8bb5ad148 --- /dev/null +++ b/test/node-http-test.js @@ -0,0 +1,154 @@ +/* eslint-env mocha */ +/* eslint prefer-arrow-callback: 'off' */ + +'use strict'; + +const assert = require('bsert'); +const consensus = require('../lib/protocol/consensus'); +const Network = require('../lib/protocol/network'); +const Coin = require('../lib/primitives/coin'); +const Script = require('../lib/script/script'); +const Opcode = require('../lib/script/opcode'); +const FullNode = require('../lib/node/fullnode'); +const Wallet = require('../lib/wallet/wallet'); +const MTX = require('../lib/primitives/mtx'); +const TX = require('../lib/primitives/tx'); +const Address = require('../lib/primitives/address'); +const rules = require('../lib/covenants/rules'); +const network = Network.get('regtest'); + +const { NodeClient, WalletClient } = require('hs-client'); + +const nclient = new NodeClient({ + port: network.rpcPort, + apiKey: 'foo' +}); + +const wclient = new WalletClient({ + port: network.walletPort, + apiKey: 'foo' +}); + +describe('Node http', function() { + this.timeout(5000); + let NAME0; + let node; + let miner; + let chain; + let NAME1; + + const mineBlocks = async (n = 1) => { + for (let i = 0; i < n; i++) { + const block = await miner.mineBlock(); + await chain.add(block); + } + }; + + beforeEach(async () => { + node = new FullNode({ + memory: true, + apiKey: 'foo', + network: 'regtest', + workers: true, + plugins: [require('../lib/wallet/plugin')] + }); + miner = node.miner; + chain = node.chain; + NAME0 = await rules.grindName(10, 0, network); + NAME1 = await rules.grindName(10, 20, network); + await node.open(); + await mineBlocks(network.names.auctionStart); + assert.equal(network.names.auctionStart, 0); + await mineBlocks(1); + }); + + afterEach(async () => { + await node.close(); + }); + + describe('getNameInfo', () => { + describe('For names that are available at height 0', () => { + it('It should return null when there hasn\'t been an auction initiated', async () => { + const nameInfo = await nclient.getNameInfo(NAME0); + assert.deepEqual(nameInfo, { + info: null, + start: { + reserved: false, + start: 0, + week: 0 + } + }); + }); + it('It should start an auction on the first day', async () => { + await mineBlocks(1); + const nameInfo = await nclient.getNameInfo(NAME0); + assert.deepEqual(nameInfo, { + info: null, + start: { + reserved: false, + start: 0, + week: 0 + } + }); + const open = await wclient.execute('sendopen', [NAME0]); + assert(open); + }); + it('It should start an auction on the 2nd day', async () => { + // Question: This test passes non-deterministically. Why? + // Note: Keeping this test as proof that the behavior of grindName + // isnt working as one would expect. + await mineBlocks(175); // Note: This number seems to pass consistently. \o.o/ + const nameInfo = await nclient.getNameInfo(NAME0); + assert.deepEqual(nameInfo, { + info: null, + start: { + reserved: false, + start: 0, + week: 0 + } + }); + const open = await wclient.execute('sendopen', [NAME0]); + assert(open); + const nameInfoBefore = await nclient.getNameInfo(NAME0); + assert.equal(nameInfoBefore.info, null); + await mineBlocks(1); + const nameInfoAfter = await nclient.getNameInfo(NAME0); + assert.equal(nameInfoAfter.info.name, NAME0); + assert.equal(nameInfoAfter.info.state, 'OPENING'); + }); + }); + + describe('For names that are available at height 20', () => { + it('It should getNameInfo for an opening name', async () => { + await mineBlocks(20); + await wclient.execute('sendopen', [NAME1]); + await mineBlocks(1); + const nameInfo = await nclient.getNameInfo(NAME1); + assert(nameInfo.start.start < 20); + assert.equal(nameInfo.start.reserved, false); + assert.equal(nameInfo.info.state, 'OPENING'); + }); + }); + }); + + describe('getNameByHash', () => { + it('It should return null when an auction has not been initiated', async () => { + const nameHash = rules.hashName(NAME0); + const name = await nclient.getNameByHash(nameHash.toString('hex')); + assert.equal(name, null); + }); + + describe('When an auction has been initiated', () => { + beforeEach(async () => { + await mineBlocks(250); + await wclient.execute('sendopen', [NAME0]); + await mineBlocks(1); + }); + it('It should return the name', async () => { + const nameHash = rules.hashName(NAME0); + const { name } = await nclient.getNameByHash(nameHash.toString('hex')); + assert.equal(name, NAME0); + }); + }); + }); +}); From 8a9fe95e53c17f621baea33100e56484e3d1d70e Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Sun, 5 May 2019 11:38:15 -0700 Subject: [PATCH 02/29] Implement grindname, getproof, and getresource --- lib/node/http.js | 59 +++++++++++++++++++++++++++++- package.json | 2 +- test/node-http-test.js | 81 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 132 insertions(+), 10 deletions(-) diff --git a/lib/node/http.js b/lib/node/http.js index 6a39bef20..6688ea716 100644 --- a/lib/node/http.js +++ b/lib/node/http.js @@ -22,6 +22,7 @@ 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 @@ -470,7 +471,7 @@ class HTTP extends Server { const hash = valid.bhash('hash'); if (!hash) - throw new Error('Invalid parameter.'); + throw new Error('Invalid hash.'); const ns = await this.chain.db.getNameState(hash); @@ -479,6 +480,62 @@ class HTTP extends Server { return res.json(200, { name: ns.name.toString('binary') }); }); + + 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); + + if (!ns || ns.data.length === 0) + return null; + + const resource = Resource.decode(ns.data); + + return res.json(200, resource.toJSON()); + }); + + this.get('/proof/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 hash = this.chain.tip.hash; + const height = this.chain.tip.height; + const root = this.chain.tip.treeRoot; + const key = rules.hashName(name); + const proof = await this.chain.db.prove(root, key); + + return res.json(200, { + hash: hash.toString('hex'), + height: height, + root: root.toString('hex'), + name: name, + key: key.toString('hex'), + proof: 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 }); + }); } /** diff --git a/package.json b/package.json index b39ca9929..dd7109dad 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "bval": "~0.1.6", "bweb": "~0.1.8", "goosig": "~0.1.0", - "hs-client": "file:/run/media/wiski/549A5F9E28D6FB4C/hs-client", + "hs-client": "~0.0.6", "mrmr": "~0.1.8", "n64": "~0.2.9", "urkel": "~0.6.3" diff --git a/test/node-http-test.js b/test/node-http-test.js index 8bb5ad148..d0ade94d9 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -4,16 +4,8 @@ 'use strict'; const assert = require('bsert'); -const consensus = require('../lib/protocol/consensus'); const Network = require('../lib/protocol/network'); -const Coin = require('../lib/primitives/coin'); -const Script = require('../lib/script/script'); -const Opcode = require('../lib/script/opcode'); const FullNode = require('../lib/node/fullnode'); -const Wallet = require('../lib/wallet/wallet'); -const MTX = require('../lib/primitives/mtx'); -const TX = require('../lib/primitives/tx'); -const Address = require('../lib/primitives/address'); const rules = require('../lib/covenants/rules'); const network = Network.get('regtest'); @@ -151,4 +143,77 @@ describe('Node http', function() { }); }); }); + + describe('getNameResource', () => { + const zonefile = { compat: false, version: 0, ttl: 172800, ns: ['ns1.example.com.@1.2.3.4'] }; + it('It should return null when an auction has not been initiated', async () => { + const resource = await nclient.getNameResource(NAME0); + assert.equal(resource, null); + }); + + describe('When an auction has been initiated', () => { + beforeEach(async () => { + await mineBlocks(250); + await wclient.execute('sendopen', [NAME0]); + // Question: We have to mine ~7 blocks here to get 'getauctioninfo' to work consistently. Will pass w. 4 + await mineBlocks(7); + const { stats: { blocksUntilBidding } } = await wclient.execute('getauctioninfo', [NAME0]); + await mineBlocks(blocksUntilBidding); + const sendBid = await wclient.execute('sendbid', [NAME0, 12, 12]); + assert(sendBid); + const { stats: { blocksUntilReveal } } = await wclient.execute('getauctioninfo', [NAME0]); + // Question: We have to mine ~2 blocks here to get 'sendreveal' to work consistently. Will pass w. 1. + await mineBlocks(blocksUntilReveal + 2); + await wclient.execute('sendreveal', [NAME0]); + const { stats: { blocksUntilClose } } = await wclient.execute('getauctioninfo', [NAME0]); + await mineBlocks(blocksUntilClose); + await wclient.execute('sendupdate', [NAME0, zonefile]); + await mineBlocks(1); + }); + it('It should return the resource', async () => { + const resource = await nclient.getNameResource(NAME0); + assert.deepEqual(resource, zonefile); + }); + }); + }); + + describe('getNameProof', () => { + it('It should return null when an auction has not been initiated', async () => { + const proof = await nclient.getNameProof(NAME0); + assert.equal(proof.proof.type, 'TYPE_DEADEND'); + assert.equal(proof.name, NAME0); + }); + + describe('When an auction has been initiated', () => { + beforeEach(async () => { + await mineBlocks(250); + await wclient.execute('sendopen', [NAME0]); + // Question: We have to mine ~7 blocks here to get 'getauctioninfo' to work consistently. Will pass w. 4 + await mineBlocks(7); + const { stats: { blocksUntilBidding } } = await wclient.execute('getauctioninfo', [NAME0]); + await mineBlocks(blocksUntilBidding); + const sendBid = await wclient.execute('sendbid', [NAME0, 12, 12]); + assert(sendBid); + const { stats: { blocksUntilReveal } } = await wclient.execute('getauctioninfo', [NAME0]); + // Question: We have to mine ~2 blocks here to get 'sendreveal' to work consistently. Will pass w. 1. + await mineBlocks(blocksUntilReveal + 2); + await wclient.execute('sendreveal', [NAME0]); + const { stats: { blocksUntilClose } } = await wclient.execute('getauctioninfo', [NAME0]); + await mineBlocks(blocksUntilClose); + await wclient.execute('sendupdate', [NAME0, { compat: false, version: 0, ttl: 172800, ns: ['ns1.example.com.@1.2.3.4'] }]); + await mineBlocks(1); + }); + it('It should return the name\'s proof', async () => { + const proof = await nclient.getNameProof(NAME0); + assert.equal(proof.proof.type, 'TYPE_EXISTS'); + assert.equal(proof.name, NAME0); + }); + }); + }); + describe('grindName', () => { + it('It should grind a name', async () => { + const { name } = await nclient.grindName(10); + assert(name); + }); + }); }); From 3fcd483cfdec26bb50a28d562ee1e624a07a5b70 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Sun, 5 May 2019 12:11:13 -0700 Subject: [PATCH 03/29] Use paired hs-client --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dd7109dad..d35db729f 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "bval": "~0.1.6", "bweb": "~0.1.8", "goosig": "~0.1.0", - "hs-client": "~0.0.6", + "hs-client": "github:wi-ski/hs-client#node-http-routes", "mrmr": "~0.1.8", "n64": "~0.2.9", "urkel": "~0.6.3" From ad782fe63d5d371bc87f3c1fa65c9afbb7227fe5 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Mon, 13 May 2019 19:21:13 -0700 Subject: [PATCH 04/29] Undo need for update hs-client --- package.json | 2 +- test/node-http-test.js | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index d35db729f..dd7109dad 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "bval": "~0.1.6", "bweb": "~0.1.8", "goosig": "~0.1.0", - "hs-client": "github:wi-ski/hs-client#node-http-routes", + "hs-client": "~0.0.6", "mrmr": "~0.1.8", "n64": "~0.2.9", "urkel": "~0.6.3" diff --git a/test/node-http-test.js b/test/node-http-test.js index d0ade94d9..e8694a961 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -12,12 +12,12 @@ const network = Network.get('regtest'); const { NodeClient, WalletClient } = require('hs-client'); const nclient = new NodeClient({ - port: network.rpcPort, + port: network.rpcPort, // Todo: Make it so this doesnt bind to any port apiKey: 'foo' }); const wclient = new WalletClient({ - port: network.walletPort, + port: network.walletPort, // Todo: Make it so this doesnt bind to any port apiKey: 'foo' }); @@ -61,7 +61,7 @@ describe('Node http', function() { describe('getNameInfo', () => { describe('For names that are available at height 0', () => { it('It should return null when there hasn\'t been an auction initiated', async () => { - const nameInfo = await nclient.getNameInfo(NAME0); + const nameInfo = await nclient.get(`/info/name/${NAME0}`); assert.deepEqual(nameInfo, { info: null, start: { @@ -73,7 +73,7 @@ describe('Node http', function() { }); it('It should start an auction on the first day', async () => { await mineBlocks(1); - const nameInfo = await nclient.getNameInfo(NAME0); + const nameInfo = await nclient.get(`/info/name/${NAME0}`); assert.deepEqual(nameInfo, { info: null, start: { @@ -90,7 +90,7 @@ describe('Node http', function() { // Note: Keeping this test as proof that the behavior of grindName // isnt working as one would expect. await mineBlocks(175); // Note: This number seems to pass consistently. \o.o/ - const nameInfo = await nclient.getNameInfo(NAME0); + const nameInfo = await nclient.get(`/info/name/${NAME0}`); assert.deepEqual(nameInfo, { info: null, start: { @@ -115,7 +115,7 @@ describe('Node http', function() { await mineBlocks(20); await wclient.execute('sendopen', [NAME1]); await mineBlocks(1); - const nameInfo = await nclient.getNameInfo(NAME1); + const nameInfo = await nclient.get(`/info/name/${NAME1}`); assert(nameInfo.start.start < 20); assert.equal(nameInfo.start.reserved, false); assert.equal(nameInfo.info.state, 'OPENING'); @@ -126,7 +126,7 @@ describe('Node http', function() { describe('getNameByHash', () => { it('It should return null when an auction has not been initiated', async () => { const nameHash = rules.hashName(NAME0); - const name = await nclient.getNameByHash(nameHash.toString('hex')); + const name = await nclient.get(`/name/hash/${nameHash.toString('hex')}`); assert.equal(name, null); }); @@ -138,7 +138,7 @@ describe('Node http', function() { }); it('It should return the name', async () => { const nameHash = rules.hashName(NAME0); - const { name } = await nclient.getNameByHash(nameHash.toString('hex')); + const { name } = await nclient.get(`/name/hash/${nameHash.toString('hex')}`); assert.equal(name, NAME0); }); }); @@ -147,7 +147,7 @@ describe('Node http', function() { describe('getNameResource', () => { const zonefile = { compat: false, version: 0, ttl: 172800, ns: ['ns1.example.com.@1.2.3.4'] }; it('It should return null when an auction has not been initiated', async () => { - const resource = await nclient.getNameResource(NAME0); + const resource = await nclient.get(`/resource/name/${NAME0}`); assert.equal(resource, null); }); @@ -171,7 +171,7 @@ describe('Node http', function() { await mineBlocks(1); }); it('It should return the resource', async () => { - const resource = await nclient.getNameResource(NAME0); + const resource = await nclient.get(`/resource/name/${NAME0}`); assert.deepEqual(resource, zonefile); }); }); @@ -179,7 +179,7 @@ describe('Node http', function() { describe('getNameProof', () => { it('It should return null when an auction has not been initiated', async () => { - const proof = await nclient.getNameProof(NAME0); + const proof = await nclient.get(`/proof/name/${NAME0}`); assert.equal(proof.proof.type, 'TYPE_DEADEND'); assert.equal(proof.name, NAME0); }); @@ -204,7 +204,7 @@ describe('Node http', function() { await mineBlocks(1); }); it('It should return the name\'s proof', async () => { - const proof = await nclient.getNameProof(NAME0); + const proof = await nclient.get(`/proof/name/${NAME0}`); assert.equal(proof.proof.type, 'TYPE_EXISTS'); assert.equal(proof.name, NAME0); }); From 1d20d2ab1e27a76c2570126c7d5100d7fd57cbfd Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Mon, 13 May 2019 21:51:47 -0700 Subject: [PATCH 05/29] Remaining getInfo calls --- test/node-http-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index e8694a961..eb693ae6f 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -101,10 +101,10 @@ describe('Node http', function() { }); const open = await wclient.execute('sendopen', [NAME0]); assert(open); - const nameInfoBefore = await nclient.getNameInfo(NAME0); + const nameInfoBefore = await nclient.get(`/info/name/${NAME0}`); assert.equal(nameInfoBefore.info, null); await mineBlocks(1); - const nameInfoAfter = await nclient.getNameInfo(NAME0); + const nameInfoAfter = await nclient.get(`/info/name/${NAME0}`); assert.equal(nameInfoAfter.info.name, NAME0); assert.equal(nameInfoAfter.info.state, 'OPENING'); }); From b9b9396da1ffe7f930081457499b5f189b834ddb Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Mon, 13 May 2019 21:58:40 -0700 Subject: [PATCH 06/29] this correction --- test/node-http-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index eb693ae6f..2c3f8117a 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -212,7 +212,7 @@ describe('Node http', function() { }); describe('grindName', () => { it('It should grind a name', async () => { - const { name } = await nclient.grindName(10); + const { name } = await nclient.get('/grind', { size: 10 }); assert(name); }); }); From 0f987594f0542b5b90ae4943de0434665adf4faf Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Tue, 14 May 2019 09:40:09 -0700 Subject: [PATCH 07/29] Update test/node-http-test.js --- test/node-http-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index 2c3f8117a..5e576aca4 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -17,7 +17,7 @@ const nclient = new NodeClient({ }); const wclient = new WalletClient({ - port: network.walletPort, // Todo: Make it so this doesnt bind to any port + port: network.walletPort, apiKey: 'foo' }); From 5db3f58d728d4601ec53772902619af1e522c989 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Tue, 14 May 2019 09:40:24 -0700 Subject: [PATCH 08/29] Update test/node-http-test.js Kill comment. --- test/node-http-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index 5e576aca4..02768cc3e 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -12,7 +12,7 @@ const network = Network.get('regtest'); const { NodeClient, WalletClient } = require('hs-client'); const nclient = new NodeClient({ - port: network.rpcPort, // Todo: Make it so this doesnt bind to any port + port: network.rpcPort, apiKey: 'foo' }); From 14a5c319f6eb4672ad1a14ea69f21b213f464673 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Tue, 14 May 2019 19:54:38 -0700 Subject: [PATCH 09/29] Surface level test tweaks, next try sleep again --- test/node-http-test.js | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index 02768cc3e..1a63f981f 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -23,11 +23,8 @@ const wclient = new WalletClient({ describe('Node http', function() { this.timeout(5000); - let NAME0; - let node; - let miner; - let chain; - let NAME1; + let NAME0, NAME1; + let node, miner, chain; const mineBlocks = async (n = 1) => { for (let i = 0; i < n; i++) { @@ -37,6 +34,7 @@ describe('Node http', function() { }; beforeEach(async () => { + assert.equal(network.names.auctionStart, 0); node = new FullNode({ memory: true, apiKey: 'foo', @@ -50,7 +48,6 @@ describe('Node http', function() { NAME1 = await rules.grindName(10, 20, network); await node.open(); await mineBlocks(network.names.auctionStart); - assert.equal(network.names.auctionStart, 0); await mineBlocks(1); }); @@ -145,14 +142,14 @@ describe('Node http', function() { }); describe('getNameResource', () => { - const zonefile = { compat: false, version: 0, ttl: 172800, ns: ['ns1.example.com.@1.2.3.4'] }; + const records = { compat: false, version: 0, ttl: 172800, ns: ['ns1.example.com.@1.2.3.4'] }; it('It should return null when an auction has not been initiated', async () => { const resource = await nclient.get(`/resource/name/${NAME0}`); assert.equal(resource, null); }); describe('When an auction has been initiated', () => { - beforeEach(async () => { + it('It should return the resource', async () => { await mineBlocks(250); await wclient.execute('sendopen', [NAME0]); // Question: We have to mine ~7 blocks here to get 'getauctioninfo' to work consistently. Will pass w. 4 @@ -167,12 +164,10 @@ describe('Node http', function() { await wclient.execute('sendreveal', [NAME0]); const { stats: { blocksUntilClose } } = await wclient.execute('getauctioninfo', [NAME0]); await mineBlocks(blocksUntilClose); - await wclient.execute('sendupdate', [NAME0, zonefile]); + await wclient.execute('sendupdate', [NAME0, records]); await mineBlocks(1); - }); - it('It should return the resource', async () => { const resource = await nclient.get(`/resource/name/${NAME0}`); - assert.deepEqual(resource, zonefile); + assert.deepEqual(resource, records); }); }); }); @@ -210,10 +205,13 @@ describe('Node http', function() { }); }); }); - describe('grindName', () => { + describe.only('grindName', () => { it('It should grind a name', async () => { - const { name } = await nclient.get('/grind', { size: 10 }); + const size = 10; + const { name } = await nclient.get('/grind', { size }); assert(name); + assert.equal(name.length, size); + assert(rules.verifyName(name)); }); }); }); From dc5e51813d0146c8182b78b7bf0aab4089266d13 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Tue, 14 May 2019 20:10:21 -0700 Subject: [PATCH 10/29] Test implementation using sleep attempt --- test/node-http-test.js | 38 +++++++++++++++----------------------- test/util/common.js | 4 ++++ 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index 1a63f981f..32554f738 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -7,6 +7,7 @@ const assert = require('bsert'); const Network = require('../lib/protocol/network'); const FullNode = require('../lib/node/fullnode'); const rules = require('../lib/covenants/rules'); +const common = require('./util/common'); const network = Network.get('regtest'); const { NodeClient, WalletClient } = require('hs-client'); @@ -31,6 +32,7 @@ describe('Node http', function() { const block = await miner.mineBlock(); await chain.add(block); } + await common.sleep(100); }; beforeEach(async () => { @@ -48,7 +50,6 @@ describe('Node http', function() { NAME1 = await rules.grindName(10, 20, network); await node.open(); await mineBlocks(network.names.auctionStart); - await mineBlocks(1); }); afterEach(async () => { @@ -69,24 +70,21 @@ describe('Node http', function() { }); }); it('It should start an auction on the first day', async () => { - await mineBlocks(1); const nameInfo = await nclient.get(`/info/name/${NAME0}`); assert.deepEqual(nameInfo, { info: null, - start: { - reserved: false, - start: 0, - week: 0 - } + start: { + reserved: false, + start: 0, + week: 0 + } }); + await mineBlocks(10); const open = await wclient.execute('sendopen', [NAME0]); assert(open); }); it('It should start an auction on the 2nd day', async () => { - // Question: This test passes non-deterministically. Why? - // Note: Keeping this test as proof that the behavior of grindName - // isnt working as one would expect. - await mineBlocks(175); // Note: This number seems to pass consistently. \o.o/ + await mineBlocks(40); const nameInfo = await nclient.get(`/info/name/${NAME0}`); assert.deepEqual(nameInfo, { info: null, @@ -128,12 +126,10 @@ describe('Node http', function() { }); describe('When an auction has been initiated', () => { - beforeEach(async () => { + it('It should return the name', async () => { await mineBlocks(250); await wclient.execute('sendopen', [NAME0]); await mineBlocks(1); - }); - it('It should return the name', async () => { const nameHash = rules.hashName(NAME0); const { name } = await nclient.get(`/name/hash/${nameHash.toString('hex')}`); assert.equal(name, NAME0); @@ -152,15 +148,13 @@ describe('Node http', function() { it('It should return the resource', async () => { await mineBlocks(250); await wclient.execute('sendopen', [NAME0]); - // Question: We have to mine ~7 blocks here to get 'getauctioninfo' to work consistently. Will pass w. 4 - await mineBlocks(7); + await mineBlocks(1); const { stats: { blocksUntilBidding } } = await wclient.execute('getauctioninfo', [NAME0]); await mineBlocks(blocksUntilBidding); const sendBid = await wclient.execute('sendbid', [NAME0, 12, 12]); assert(sendBid); const { stats: { blocksUntilReveal } } = await wclient.execute('getauctioninfo', [NAME0]); - // Question: We have to mine ~2 blocks here to get 'sendreveal' to work consistently. Will pass w. 1. - await mineBlocks(blocksUntilReveal + 2); + await mineBlocks(blocksUntilReveal); await wclient.execute('sendreveal', [NAME0]); const { stats: { blocksUntilClose } } = await wclient.execute('getauctioninfo', [NAME0]); await mineBlocks(blocksUntilClose); @@ -183,15 +177,13 @@ describe('Node http', function() { beforeEach(async () => { await mineBlocks(250); await wclient.execute('sendopen', [NAME0]); - // Question: We have to mine ~7 blocks here to get 'getauctioninfo' to work consistently. Will pass w. 4 - await mineBlocks(7); + await mineBlocks(1); const { stats: { blocksUntilBidding } } = await wclient.execute('getauctioninfo', [NAME0]); await mineBlocks(blocksUntilBidding); const sendBid = await wclient.execute('sendbid', [NAME0, 12, 12]); assert(sendBid); const { stats: { blocksUntilReveal } } = await wclient.execute('getauctioninfo', [NAME0]); - // Question: We have to mine ~2 blocks here to get 'sendreveal' to work consistently. Will pass w. 1. - await mineBlocks(blocksUntilReveal + 2); + await mineBlocks(blocksUntilReveal); await wclient.execute('sendreveal', [NAME0]); const { stats: { blocksUntilClose } } = await wclient.execute('getauctioninfo', [NAME0]); await mineBlocks(blocksUntilClose); @@ -205,7 +197,7 @@ describe('Node http', function() { }); }); }); - describe.only('grindName', () => { + describe('grindName', () => { it('It should grind a name', async () => { const size = 10; const { name } = await nclient.get('/grind', { size }); diff --git a/test/util/common.js b/test/util/common.js index 050fbcf98..5f911c444 100644 --- a/test/util/common.js +++ b/test/util/common.js @@ -113,6 +113,10 @@ common.forValue = async function(obj, key, val, timeout = 30000) { }); }; +common.sleep = function sleep(time) { + return new Promise(resolve => setTimeout(resolve, time)); +}; + function parseUndo(data) { const br = bio.read(data); const items = []; From 0722801c7899d5904a5212e31d52141d294931bf Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Tue, 14 May 2019 21:30:35 -0700 Subject: [PATCH 11/29] Kill before each --- test/node-http-test.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index 32554f738..ed037376d 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -174,7 +174,7 @@ describe('Node http', function() { }); describe('When an auction has been initiated', () => { - beforeEach(async () => { + it('It should return the name\'s proof', async () => { await mineBlocks(250); await wclient.execute('sendopen', [NAME0]); await mineBlocks(1); @@ -189,8 +189,6 @@ describe('Node http', function() { await mineBlocks(blocksUntilClose); await wclient.execute('sendupdate', [NAME0, { compat: false, version: 0, ttl: 172800, ns: ['ns1.example.com.@1.2.3.4'] }]); await mineBlocks(1); - }); - it('It should return the name\'s proof', async () => { const proof = await nclient.get(`/proof/name/${NAME0}`); assert.equal(proof.proof.type, 'TYPE_EXISTS'); assert.equal(proof.name, NAME0); From ffb2fc118d9d125d225b35130cffa46f682634dc Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Tue, 14 May 2019 21:35:07 -0700 Subject: [PATCH 12/29] Extend timeout? --- test/node-http-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index ed037376d..8ebe164fc 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -23,7 +23,7 @@ const wclient = new WalletClient({ }); describe('Node http', function() { - this.timeout(5000); + this.timeout(10000); let NAME0, NAME1; let node, miner, chain; From 0fdce37a6be303a75561e9d2a155aa0cadc0d2ce Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Tue, 14 May 2019 21:39:09 -0700 Subject: [PATCH 13/29] Extend timeout again. --- test/node-http-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index 8ebe164fc..a8f3147a9 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -23,7 +23,7 @@ const wclient = new WalletClient({ }); describe('Node http', function() { - this.timeout(10000); + this.timeout(15000); let NAME0, NAME1; let node, miner, chain; From 6baaa6d64b58030b6e950e3e693702b51b197dc5 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Tue, 14 May 2019 22:00:04 -0700 Subject: [PATCH 14/29] Event listening --- test/node-http-test.js | 9 +++++++++ test/util/common.js | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/test/node-http-test.js b/test/node-http-test.js index a8f3147a9..53207b288 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -24,13 +24,16 @@ const wclient = new WalletClient({ describe('Node http', function() { this.timeout(15000); + const witnessedBlockHashes = {}; let NAME0, NAME1; let node, miner, chain; const mineBlocks = async (n = 1) => { for (let i = 0; i < n; i++) { const block = await miner.mineBlock(); + const blockHash = block.hash().toString('hex'); await chain.add(block); + await common.forValue(witnessedBlockHashes, blockHash, blockHash); } await common.sleep(100); }; @@ -44,6 +47,12 @@ describe('Node http', function() { workers: true, plugins: [require('../lib/wallet/plugin')] }); + + node.on('connect', (entry, block) => { + const blockHash = block.hash().toString('hex'); + witnessedBlockHashes[blockHash] = blockHash; + }); + miner = node.miner; chain = node.chain; NAME0 = await rules.grindName(10, 0, network); diff --git a/test/util/common.js b/test/util/common.js index 5f911c444..18fb29f25 100644 --- a/test/util/common.js +++ b/test/util/common.js @@ -117,6 +117,27 @@ common.sleep = function sleep(time) { return new Promise(resolve => setTimeout(resolve, time)); }; +common.forValue = async function(obj, key, val, timeout = 30000) { + assert(typeof obj === 'object'); + assert(typeof key === 'string'); + + const ms = 10; + let interval = null; + let count = 0; + return new Promise((resolve, reject) => { + interval = setInterval(() => { + if (obj[key] === val) { + clearInterval(interval); + resolve(); + } else if (count * ms >= timeout) { + clearInterval(interval); + reject(new Error('Timeout waiting for value.')); + } + count += 1; + }, ms); + }); +}; + function parseUndo(data) { const br = bio.read(data); const items = []; From b0351030f8e7f8032b0a25221bbe8bb3e877348d Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Tue, 14 May 2019 22:01:35 -0700 Subject: [PATCH 15/29] First pass at mining fewer blocks --- test/node-http-test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index 53207b288..7ae090048 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -136,7 +136,7 @@ describe('Node http', function() { describe('When an auction has been initiated', () => { it('It should return the name', async () => { - await mineBlocks(250); + await mineBlocks(50); await wclient.execute('sendopen', [NAME0]); await mineBlocks(1); const nameHash = rules.hashName(NAME0); @@ -155,7 +155,7 @@ describe('Node http', function() { describe('When an auction has been initiated', () => { it('It should return the resource', async () => { - await mineBlocks(250); + await mineBlocks(50); await wclient.execute('sendopen', [NAME0]); await mineBlocks(1); const { stats: { blocksUntilBidding } } = await wclient.execute('getauctioninfo', [NAME0]); @@ -184,7 +184,7 @@ describe('Node http', function() { describe('When an auction has been initiated', () => { it('It should return the name\'s proof', async () => { - await mineBlocks(250); + await mineBlocks(50); await wclient.execute('sendopen', [NAME0]); await mineBlocks(1); const { stats: { blocksUntilBidding } } = await wclient.execute('getauctioninfo', [NAME0]); From 721f8fd426305eeb1f74ded5c89861b659a57910 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Tue, 14 May 2019 22:03:56 -0700 Subject: [PATCH 16/29] Remove sleep --- test/node-http-test.js | 1 - test/util/common.js | 22 ---------------------- 2 files changed, 23 deletions(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index 7ae090048..9e3d0920c 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -35,7 +35,6 @@ describe('Node http', function() { await chain.add(block); await common.forValue(witnessedBlockHashes, blockHash, blockHash); } - await common.sleep(100); }; beforeEach(async () => { diff --git a/test/util/common.js b/test/util/common.js index 18fb29f25..d89f9c01b 100644 --- a/test/util/common.js +++ b/test/util/common.js @@ -91,28 +91,6 @@ common.event = async function event(obj, name) { }); }; -common.forValue = async function(obj, key, val, timeout = 30000) { - assert(typeof obj === 'object'); - assert(typeof key === 'string'); - - const ms = 10; - let interval = null; - let count = 0; - - return new Promise((resolve, reject) => { - interval = setInterval(() => { - if (obj[key] === val) { - clearInterval(interval); - resolve(); - } else if (count * ms >= timeout) { - clearInterval(interval); - reject(new Error('Timeout waiting for value.')); - } - count += 1; - }, ms); - }); -}; - common.sleep = function sleep(time) { return new Promise(resolve => setTimeout(resolve, time)); }; From 593a151730d66bed8237e0ae1f37fee775636955 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Tue, 14 May 2019 22:11:03 -0700 Subject: [PATCH 17/29] Mine even fewer blocks --- test/node-http-test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index 9e3d0920c..591fde33b 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -135,7 +135,7 @@ describe('Node http', function() { describe('When an auction has been initiated', () => { it('It should return the name', async () => { - await mineBlocks(50); + await mineBlocks(10); await wclient.execute('sendopen', [NAME0]); await mineBlocks(1); const nameHash = rules.hashName(NAME0); @@ -154,7 +154,7 @@ describe('Node http', function() { describe('When an auction has been initiated', () => { it('It should return the resource', async () => { - await mineBlocks(50); + await mineBlocks(10); await wclient.execute('sendopen', [NAME0]); await mineBlocks(1); const { stats: { blocksUntilBidding } } = await wclient.execute('getauctioninfo', [NAME0]); @@ -183,7 +183,7 @@ describe('Node http', function() { describe('When an auction has been initiated', () => { it('It should return the name\'s proof', async () => { - await mineBlocks(50); + await mineBlocks(10); await wclient.execute('sendopen', [NAME0]); await mineBlocks(1); const { stats: { blocksUntilBidding } } = await wclient.execute('getauctioninfo', [NAME0]); From 5fd20d55a50515278fb4ae56e082ad5042454478 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Tue, 14 May 2019 22:14:46 -0700 Subject: [PATCH 18/29] Bring test timeout back down to reflect time seen in CI --- test/node-http-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index 591fde33b..f86511777 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -23,7 +23,7 @@ const wclient = new WalletClient({ }); describe('Node http', function() { - this.timeout(15000); + this.timeout(10000); const witnessedBlockHashes = {}; let NAME0, NAME1; let node, miner, chain; From bccab33f5af6945a67250f61137a30a724a90990 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Thu, 6 Jun 2019 22:42:46 -0700 Subject: [PATCH 19/29] PR tweaks for Tynes. Use getJSON util method, variable renaming --- lib/node/http.js | 16 +++++++++------- test/node-http-test.js | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/node/http.js b/lib/node/http.js index 6688ea716..5ced768b7 100644 --- a/lib/node/http.js +++ b/lib/node/http.js @@ -474,11 +474,13 @@ class HTTP extends Server { 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) return res.json(404); - return res.json(200, { name: ns.name.toString('binary') }); + return res.json(200, ns.getJSON(height,network)); }); this.get('/resource/name/:name', async (req, res) => { @@ -496,7 +498,7 @@ class HTTP extends Server { const resource = Resource.decode(ns.data); - return res.json(200, resource.toJSON()); + return res.json(200, resource.getJSON(name)); }); this.get('/proof/name/:name', async (req, res) => { @@ -506,18 +508,18 @@ class HTTP extends Server { if (!name || !rules.verifyName(name)) throw new Error('Invalid name.'); - const hash = this.chain.tip.hash; + const tip = this.chain.tip.hash; const height = this.chain.tip.height; const root = this.chain.tip.treeRoot; - const key = rules.hashName(name); - const proof = await this.chain.db.prove(root, key); + const nameHash = rules.hashName(name); + const proof = await this.chain.db.prove(root, nameHash); return res.json(200, { - hash: hash.toString('hex'), + hash: tip.toString('hex'), height: height, root: root.toString('hex'), name: name, - key: key.toString('hex'), + key: nameHash.toString('hex'), proof: proof.toJSON() }); }); diff --git a/test/node-http-test.js b/test/node-http-test.js index f86511777..ae08fca51 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -169,7 +169,7 @@ describe('Node http', function() { await wclient.execute('sendupdate', [NAME0, records]); await mineBlocks(1); const resource = await nclient.get(`/resource/name/${NAME0}`); - assert.deepEqual(resource, records); + assert.deepEqual(resource, { ...records, name: NAME0, version: 0 }); }); }); }); From 32d85579161e3c209c93b1bf993be6c3b3df3dd1 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Fri, 7 Jun 2019 21:00:20 -0700 Subject: [PATCH 20/29] Try CI again --- test/node-http-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index ae08fca51..4eb359c49 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -24,7 +24,7 @@ const wclient = new WalletClient({ describe('Node http', function() { this.timeout(10000); - const witnessedBlockHashes = {}; + const witnessedBlocks = {}; let NAME0, NAME1; let node, miner, chain; @@ -33,7 +33,7 @@ describe('Node http', function() { const block = await miner.mineBlock(); const blockHash = block.hash().toString('hex'); await chain.add(block); - await common.forValue(witnessedBlockHashes, blockHash, blockHash); + await common.forValue(witnessedBlocks, blockHash, blockHash); } }; From c0bec639e79af1caaac74a58c7ca995b6dc70041 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Wed, 26 Jun 2019 22:50:16 -0700 Subject: [PATCH 21/29] Fix missing ref --- test/node-http-test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index 4eb359c49..c56cdae68 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -47,6 +47,7 @@ describe('Node http', function() { plugins: [require('../lib/wallet/plugin')] }); + const witnessedBlockHashes = {}; node.on('connect', (entry, block) => { const blockHash = block.hash().toString('hex'); witnessedBlockHashes[blockHash] = blockHash; @@ -169,7 +170,7 @@ describe('Node http', function() { await wclient.execute('sendupdate', [NAME0, records]); await mineBlocks(1); const resource = await nclient.get(`/resource/name/${NAME0}`); - assert.deepEqual(resource, { ...records, name: NAME0, version: 0 }); + assert.deepEqual(resource, Object.assign(records, { name: NAME0, version: 0 })); }); }); }); From 9050228f5610747ab3bbcca1b4f5d2072d6f4279 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Wed, 26 Jun 2019 23:03:15 -0700 Subject: [PATCH 22/29] Fix for var shadowing --- test/node-http-test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index c56cdae68..70e518d08 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -47,10 +47,9 @@ describe('Node http', function() { plugins: [require('../lib/wallet/plugin')] }); - const witnessedBlockHashes = {}; node.on('connect', (entry, block) => { const blockHash = block.hash().toString('hex'); - witnessedBlockHashes[blockHash] = blockHash; + witnessedBlocks[blockHash] = blockHash; }); miner = node.miner; From 3b8a8a478d0032a696030add85f72c86fa49586b Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Sun, 30 Jun 2019 16:36:43 -0700 Subject: [PATCH 23/29] Update routes, use more determnistic mining setup, implement and test getnameproofbyhash --- lib/node/http.js | 25 ++++++- test/node-http-test.js | 155 +++++++++++++++++++++++++++++---------- test/util/common.js | 14 ++++ test/wallet-http-test.js | 15 +--- 4 files changed, 154 insertions(+), 55 deletions(-) diff --git a/lib/node/http.js b/lib/node/http.js index 5ced768b7..18acc71c9 100644 --- a/lib/node/http.js +++ b/lib/node/http.js @@ -435,7 +435,7 @@ class HTTP extends Server { res.json(200, { success: true }); }); - this.get('/info/name/:name', async (req, res) => { + this.get('/name/:name', async (req, res) => { const valid = Validator.fromRequest(req); const name = valid.str('name'); @@ -466,7 +466,7 @@ class HTTP extends Server { }); }); - this.get('/name/hash/:hash', async (req, res) => { + this.get('/resource/hash/:hash', async (req, res) => { const valid = Validator.fromRequest(req); const hash = valid.bhash('hash'); @@ -524,6 +524,27 @@ class HTTP extends Server { }); }); + this.get('/proof/hash/:nameHash', async (req, res) => { + 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); + + return res.json(200, { + hash: tip.toString('hex'), + height: height, + root: root.toString('hex'), + key: nameHash.toString('hex'), + proof: proof.toJSON() + }); + }); + this.get('/grind', async (req, res) => { const valid = Validator.fromRequest(req); const size = valid.u32('size'); diff --git a/test/node-http-test.js b/test/node-http-test.js index 70e518d08..8cfb9da99 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -22,23 +22,17 @@ const wclient = new WalletClient({ apiKey: 'foo' }); +const wallet = wclient.wallet('primary'); + describe('Node http', function() { this.timeout(10000); const witnessedBlocks = {}; let NAME0, NAME1; - let node, miner, chain; - - const mineBlocks = async (n = 1) => { - for (let i = 0; i < n; i++) { - const block = await miner.mineBlock(); - const blockHash = block.hash().toString('hex'); - await chain.add(block); - await common.forValue(witnessedBlocks, blockHash, blockHash); - } - }; + let node, mineBlocks, cbAddress; beforeEach(async () => { assert.equal(network.names.auctionStart, 0); + node = new FullNode({ memory: true, apiKey: 'foo', @@ -52,12 +46,15 @@ describe('Node http', function() { witnessedBlocks[blockHash] = blockHash; }); - miner = node.miner; - chain = node.chain; + mineBlocks = common.constructBlockMiner(node, nclient); NAME0 = await rules.grindName(10, 0, network); NAME1 = await rules.grindName(10, 20, network); + await node.open(); - await mineBlocks(network.names.auctionStart); + + cbAddress = (await wallet.createAddress('default')).address; + + await mineBlocks(network.names.auctionStart, cbAddress); }); afterEach(async () => { @@ -67,7 +64,7 @@ describe('Node http', function() { describe('getNameInfo', () => { describe('For names that are available at height 0', () => { it('It should return null when there hasn\'t been an auction initiated', async () => { - const nameInfo = await nclient.get(`/info/name/${NAME0}`); + const nameInfo = await nclient.get(`/name/${NAME0}`); assert.deepEqual(nameInfo, { info: null, start: { @@ -78,7 +75,7 @@ describe('Node http', function() { }); }); it('It should start an auction on the first day', async () => { - const nameInfo = await nclient.get(`/info/name/${NAME0}`); + const nameInfo = await nclient.get(`/name/${NAME0}`); assert.deepEqual(nameInfo, { info: null, start: { @@ -87,13 +84,16 @@ describe('Node http', function() { week: 0 } }); - await mineBlocks(10); + + await mineBlocks(10, cbAddress); + const open = await wclient.execute('sendopen', [NAME0]); assert(open); }); it('It should start an auction on the 2nd day', async () => { - await mineBlocks(40); - const nameInfo = await nclient.get(`/info/name/${NAME0}`); + await mineBlocks(40, cbAddress); + + const nameInfo = await nclient.get(`/name/${NAME0}`); assert.deepEqual(nameInfo, { info: null, start: { @@ -102,12 +102,16 @@ describe('Node http', function() { week: 0 } }); + const open = await wclient.execute('sendopen', [NAME0]); assert(open); - const nameInfoBefore = await nclient.get(`/info/name/${NAME0}`); + + const nameInfoBefore = await nclient.get(`/name/${NAME0}`); assert.equal(nameInfoBefore.info, null); - await mineBlocks(1); - const nameInfoAfter = await nclient.get(`/info/name/${NAME0}`); + + await mineBlocks(1, cbAddress); + + const nameInfoAfter = await nclient.get(`/name/${NAME0}`); assert.equal(nameInfoAfter.info.name, NAME0); assert.equal(nameInfoAfter.info.state, 'OPENING'); }); @@ -115,10 +119,13 @@ describe('Node http', function() { describe('For names that are available at height 20', () => { it('It should getNameInfo for an opening name', async () => { - await mineBlocks(20); + await mineBlocks(20, cbAddress); + await wclient.execute('sendopen', [NAME1]); - await mineBlocks(1); - const nameInfo = await nclient.get(`/info/name/${NAME1}`); + + await mineBlocks(1, cbAddress); + + const nameInfo = await nclient.get(`/name/${NAME1}`); assert(nameInfo.start.start < 20); assert.equal(nameInfo.start.reserved, false); assert.equal(nameInfo.info.state, 'OPENING'); @@ -129,17 +136,20 @@ describe('Node http', function() { describe('getNameByHash', () => { it('It should return null when an auction has not been initiated', async () => { const nameHash = rules.hashName(NAME0); - const name = await nclient.get(`/name/hash/${nameHash.toString('hex')}`); + const name = await nclient.get(`/resource/hash/${nameHash.toString('hex')}`); assert.equal(name, null); }); describe('When an auction has been initiated', () => { it('It should return the name', async () => { - await mineBlocks(10); + await mineBlocks(10, cbAddress); + await wclient.execute('sendopen', [NAME0]); - await mineBlocks(1); + + await mineBlocks(1, cbAddress); + const nameHash = rules.hashName(NAME0); - const { name } = await nclient.get(`/name/hash/${nameHash.toString('hex')}`); + const { name } = await nclient.get(`/resource/hash/${nameHash.toString('hex')}`); assert.equal(name, NAME0); }); }); @@ -154,20 +164,32 @@ describe('Node http', function() { describe('When an auction has been initiated', () => { it('It should return the resource', async () => { - await mineBlocks(10); + await mineBlocks(10, cbAddress); + await wclient.execute('sendopen', [NAME0]); - await mineBlocks(1); + + await mineBlocks(1, cbAddress); + const { stats: { blocksUntilBidding } } = await wclient.execute('getauctioninfo', [NAME0]); - await mineBlocks(blocksUntilBidding); + + await mineBlocks(blocksUntilBidding, cbAddress); + const sendBid = await wclient.execute('sendbid', [NAME0, 12, 12]); assert(sendBid); + const { stats: { blocksUntilReveal } } = await wclient.execute('getauctioninfo', [NAME0]); - await mineBlocks(blocksUntilReveal); + + await mineBlocks(blocksUntilReveal, cbAddress); + await wclient.execute('sendreveal', [NAME0]); const { stats: { blocksUntilClose } } = await wclient.execute('getauctioninfo', [NAME0]); - await mineBlocks(blocksUntilClose); + + await mineBlocks(blocksUntilClose, cbAddress); + await wclient.execute('sendupdate', [NAME0, records]); - await mineBlocks(1); + + await mineBlocks(1, cbAddress); + const resource = await nclient.get(`/resource/name/${NAME0}`); assert.deepEqual(resource, Object.assign(records, { name: NAME0, version: 0 })); }); @@ -183,26 +205,79 @@ describe('Node http', function() { describe('When an auction has been initiated', () => { it('It should return the name\'s proof', async () => { - await mineBlocks(10); + await mineBlocks(10, cbAddress); + await wclient.execute('sendopen', [NAME0]); - await mineBlocks(1); + + await mineBlocks(1, cbAddress); + const { stats: { blocksUntilBidding } } = await wclient.execute('getauctioninfo', [NAME0]); - await mineBlocks(blocksUntilBidding); + + await mineBlocks(blocksUntilBidding, cbAddress); + const sendBid = await wclient.execute('sendbid', [NAME0, 12, 12]); assert(sendBid); const { stats: { blocksUntilReveal } } = await wclient.execute('getauctioninfo', [NAME0]); - await mineBlocks(blocksUntilReveal); + + await mineBlocks(blocksUntilReveal, cbAddress); + await wclient.execute('sendreveal', [NAME0]); const { stats: { blocksUntilClose } } = await wclient.execute('getauctioninfo', [NAME0]); - await mineBlocks(blocksUntilClose); + + await mineBlocks(blocksUntilClose, cbAddress); + await wclient.execute('sendupdate', [NAME0, { compat: false, version: 0, ttl: 172800, ns: ['ns1.example.com.@1.2.3.4'] }]); - await mineBlocks(1); + + await mineBlocks(1, cbAddress); + const proof = await nclient.get(`/proof/name/${NAME0}`); assert.equal(proof.proof.type, 'TYPE_EXISTS'); assert.equal(proof.name, NAME0); }); }); }); + + describe.only('getNameProofByHash', () => { + it('It should return null when an auction has not been initiated', async () => { + const nameHash = rules.hashName(NAME0).toString('hex'); + const proof = await nclient.get(`/proof/hash/${nameHash}`); + assert.equal(proof.proof.type, 'TYPE_DEADEND'); + }); + + describe('When an auction has been initiated', () => { + it('It should return the name\'s proof', async () => { + await mineBlocks(10, cbAddress); + + await wclient.execute('sendopen', [NAME0]); + + await mineBlocks(1, cbAddress); + + const { stats: { blocksUntilBidding } } = await wclient.execute('getauctioninfo', [NAME0]); + + await mineBlocks(blocksUntilBidding, cbAddress); + + const sendBid = await wclient.execute('sendbid', [NAME0, 12, 12]); + assert(sendBid); + const { stats: { blocksUntilReveal } } = await wclient.execute('getauctioninfo', [NAME0]); + + await mineBlocks(blocksUntilReveal, cbAddress); + + await wclient.execute('sendreveal', [NAME0]); + const { stats: { blocksUntilClose } } = await wclient.execute('getauctioninfo', [NAME0]); + + await mineBlocks(blocksUntilClose, cbAddress); + + await wclient.execute('sendupdate', [NAME0, { compat: false, version: 0, ttl: 172800, ns: ['ns1.example.com.@1.2.3.4'] }]); + + await mineBlocks(1, cbAddress); + + const nameHash = rules.hashName(NAME0).toString('hex'); + const proof = await nclient.get(`/proof/hash/${nameHash}`); + assert.equal(proof.proof.type, 'TYPE_EXISTS'); + }); + }); + }); + describe('grindName', () => { it('It should grind a name', async () => { const size = 10; diff --git a/test/util/common.js b/test/util/common.js index d89f9c01b..56a481273 100644 --- a/test/util/common.js +++ b/test/util/common.js @@ -116,6 +116,20 @@ common.forValue = async function(obj, key, val, timeout = 30000) { }); }; +common.constructBlockMiner = function (node, nclient) { + // take into account race conditions + return async function mineBlocks(count, address) { + for (let i = 0; i < count; i++) { + const obj = { complete: false }; + node.once('block', () => { + obj.complete = true; + }); + await nclient.execute('generatetoaddress', [1, address]); + await common.forValue(obj, 'complete', true); + } + }; +}; + function parseUndo(data) { const br = bio.read(data); const items = []; diff --git a/test/wallet-http-test.js b/test/wallet-http-test.js index 9ecfcb80c..956a7cbcf 100644 --- a/test/wallet-http-test.js +++ b/test/wallet-http-test.js @@ -47,7 +47,7 @@ const wclient = new WalletClient({ const wallet = wclient.wallet('primary'); const wallet2 = wclient.wallet('secondary'); -let name, cbAddress; +let name, cbAddress, mineBlocks; const accountTwo = 'foobar'; const { @@ -79,6 +79,7 @@ describe('Wallet HTTP', function() { }); beforeEach(async () => { + mineBlocks = common.constructBlockMiner(node, nclient); name = await nclient.execute('grindname', [5]); }); @@ -1098,18 +1099,6 @@ async function sleep(time) { return new Promise(resolve => setTimeout(resolve, time)); } -// take into account race conditions -async function mineBlocks(count, address) { - for (let i = 0; i < count; i++) { - const obj = { complete: false }; - node.once('block', () => { - obj.complete = true; - }); - await nclient.execute('generatetoaddress', [1, address]); - await common.forValue(obj, 'complete', true); - } -} - // create an OPEN output function openOutput(name, address) { const nameHash = rules.hashName(name); From 4dbf7f78c4ea91e09e90e6c57750bc790722fd39 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Sun, 30 Jun 2019 16:39:12 -0700 Subject: [PATCH 24/29] Update test descriptions for TYPE_DEADEND --- test/node-http-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index 8cfb9da99..2d67e548c 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -197,7 +197,7 @@ describe('Node http', function() { }); describe('getNameProof', () => { - it('It should return null when an auction has not been initiated', async () => { + it('It should return a proof type of TYPE_DEADEND when an auction has not been initiated', async () => { const proof = await nclient.get(`/proof/name/${NAME0}`); assert.equal(proof.proof.type, 'TYPE_DEADEND'); assert.equal(proof.name, NAME0); @@ -238,7 +238,7 @@ describe('Node http', function() { }); describe.only('getNameProofByHash', () => { - it('It should return null when an auction has not been initiated', async () => { + it('It should return a proof type of TYPE_DEADEND when an auction has not been initiated', async () => { const nameHash = rules.hashName(NAME0).toString('hex'); const proof = await nclient.get(`/proof/hash/${nameHash}`); assert.equal(proof.proof.type, 'TYPE_DEADEND'); From 41f5088466db339cd9b3592352100fa53919c3bf Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Sun, 30 Jun 2019 16:45:01 -0700 Subject: [PATCH 25/29] Tighten test line spacing up --- test/node-http-test.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index 2d67e548c..ed6e63506 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -108,7 +108,6 @@ describe('Node http', function() { const nameInfoBefore = await nclient.get(`/name/${NAME0}`); assert.equal(nameInfoBefore.info, null); - await mineBlocks(1, cbAddress); const nameInfoAfter = await nclient.get(`/name/${NAME0}`); @@ -122,7 +121,6 @@ describe('Node http', function() { await mineBlocks(20, cbAddress); await wclient.execute('sendopen', [NAME1]); - await mineBlocks(1, cbAddress); const nameInfo = await nclient.get(`/name/${NAME1}`); @@ -145,7 +143,6 @@ describe('Node http', function() { await mineBlocks(10, cbAddress); await wclient.execute('sendopen', [NAME0]); - await mineBlocks(1, cbAddress); const nameHash = rules.hashName(NAME0); @@ -167,7 +164,6 @@ describe('Node http', function() { await mineBlocks(10, cbAddress); await wclient.execute('sendopen', [NAME0]); - await mineBlocks(1, cbAddress); const { stats: { blocksUntilBidding } } = await wclient.execute('getauctioninfo', [NAME0]); @@ -187,7 +183,6 @@ describe('Node http', function() { await mineBlocks(blocksUntilClose, cbAddress); await wclient.execute('sendupdate', [NAME0, records]); - await mineBlocks(1, cbAddress); const resource = await nclient.get(`/resource/name/${NAME0}`); @@ -208,7 +203,6 @@ describe('Node http', function() { await mineBlocks(10, cbAddress); await wclient.execute('sendopen', [NAME0]); - await mineBlocks(1, cbAddress); const { stats: { blocksUntilBidding } } = await wclient.execute('getauctioninfo', [NAME0]); @@ -227,7 +221,6 @@ describe('Node http', function() { await mineBlocks(blocksUntilClose, cbAddress); await wclient.execute('sendupdate', [NAME0, { compat: false, version: 0, ttl: 172800, ns: ['ns1.example.com.@1.2.3.4'] }]); - await mineBlocks(1, cbAddress); const proof = await nclient.get(`/proof/name/${NAME0}`); @@ -249,7 +242,6 @@ describe('Node http', function() { await mineBlocks(10, cbAddress); await wclient.execute('sendopen', [NAME0]); - await mineBlocks(1, cbAddress); const { stats: { blocksUntilBidding } } = await wclient.execute('getauctioninfo', [NAME0]); @@ -268,7 +260,6 @@ describe('Node http', function() { await mineBlocks(blocksUntilClose, cbAddress); await wclient.execute('sendupdate', [NAME0, { compat: false, version: 0, ttl: 172800, ns: ['ns1.example.com.@1.2.3.4'] }]); - await mineBlocks(1, cbAddress); const nameHash = rules.hashName(NAME0).toString('hex'); From d4015a5dd025c478550c70e3c1481a25b44343dd Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Sun, 30 Jun 2019 19:50:06 -0700 Subject: [PATCH 26/29] Lint fix --- test/node-http-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node-http-test.js b/test/node-http-test.js index ed6e63506..ea7c8139f 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -144,7 +144,7 @@ describe('Node http', function() { await wclient.execute('sendopen', [NAME0]); await mineBlocks(1, cbAddress); - + const nameHash = rules.hashName(NAME0); const { name } = await nclient.get(`/resource/hash/${nameHash.toString('hex')}`); assert.equal(name, NAME0); From 4c6afb41d77e61ef07565b51d7ce7efa19e631c2 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Sun, 30 Jun 2019 20:05:47 -0700 Subject: [PATCH 27/29] Return null when DB doesnt know about name --- lib/node/http.js | 4 +++- test/node-http-test.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/node/http.js b/lib/node/http.js index 18acc71c9..fe217c800 100644 --- a/lib/node/http.js +++ b/lib/node/http.js @@ -535,13 +535,15 @@ class HTTP extends Server { 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'), - proof: proof.toJSON() + proof: proof.toJSON(), + name: ns ? ns.name.toString() : null }); }); diff --git a/test/node-http-test.js b/test/node-http-test.js index ea7c8139f..c40e82341 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -230,11 +230,12 @@ describe('Node http', function() { }); }); - describe.only('getNameProofByHash', () => { + describe('getNameProofByHash', () => { it('It should return a proof type of TYPE_DEADEND when an auction has not been initiated', async () => { const nameHash = rules.hashName(NAME0).toString('hex'); const proof = await nclient.get(`/proof/hash/${nameHash}`); assert.equal(proof.proof.type, 'TYPE_DEADEND'); + assert.equal(proof.name, null); }); describe('When an auction has been initiated', () => { @@ -265,6 +266,7 @@ describe('Node http', function() { const nameHash = rules.hashName(NAME0).toString('hex'); const proof = await nclient.get(`/proof/hash/${nameHash}`); assert.equal(proof.proof.type, 'TYPE_EXISTS'); + assert.equal(proof.name, NAME0); }); }); }); From 231e30eef2c442d4914c57cddd840a03624abac9 Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Sun, 30 Jun 2019 20:07:49 -0700 Subject: [PATCH 28/29] Restore empty line --- test/util/common.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/util/common.js b/test/util/common.js index 56a481273..431b6479f 100644 --- a/test/util/common.js +++ b/test/util/common.js @@ -102,6 +102,7 @@ common.forValue = async function(obj, key, val, timeout = 30000) { const ms = 10; let interval = null; let count = 0; + return new Promise((resolve, reject) => { interval = setInterval(() => { if (obj[key] === val) { From 177a78e2b6ed04b0f99fe44e4714f7ea1b41cd7f Mon Sep 17 00:00:00 2001 From: Will Dembinski Date: Tue, 23 Jul 2019 21:00:10 -0700 Subject: [PATCH 29/29] Dedup proof response vals, shorten line length --- lib/node/http.js | 6 +++--- test/node-http-test.js | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/node/http.js b/lib/node/http.js index fe217c800..2c6aa9f24 100644 --- a/lib/node/http.js +++ b/lib/node/http.js @@ -520,7 +520,7 @@ class HTTP extends Server { root: root.toString('hex'), name: name, key: nameHash.toString('hex'), - proof: proof.toJSON() + ...proof.toJSON() }); }); @@ -542,8 +542,8 @@ class HTTP extends Server { height: height, root: root.toString('hex'), key: nameHash.toString('hex'), - proof: proof.toJSON(), - name: ns ? ns.name.toString() : null + name: ns ? ns.name.toString() : null, + ...proof.toJSON() }); }); diff --git a/test/node-http-test.js b/test/node-http-test.js index c40e82341..7dc19d100 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -194,12 +194,13 @@ describe('Node http', function() { describe('getNameProof', () => { it('It should return a proof type of TYPE_DEADEND when an auction has not been initiated', async () => { const proof = await nclient.get(`/proof/name/${NAME0}`); - assert.equal(proof.proof.type, 'TYPE_DEADEND'); + assert.equal(proof.type, 'TYPE_DEADEND'); assert.equal(proof.name, NAME0); }); describe('When an auction has been initiated', () => { it('It should return the name\'s proof', async () => { + const record = { compat: false, version: 0, ttl: 172800, ns: ['ns1.example.com.@1.2.3.4'] }; await mineBlocks(10, cbAddress); await wclient.execute('sendopen', [NAME0]); @@ -220,11 +221,11 @@ describe('Node http', function() { await mineBlocks(blocksUntilClose, cbAddress); - await wclient.execute('sendupdate', [NAME0, { compat: false, version: 0, ttl: 172800, ns: ['ns1.example.com.@1.2.3.4'] }]); + await wclient.execute('sendupdate', [NAME0, record]); await mineBlocks(1, cbAddress); const proof = await nclient.get(`/proof/name/${NAME0}`); - assert.equal(proof.proof.type, 'TYPE_EXISTS'); + assert.equal(proof.type, 'TYPE_EXISTS'); assert.equal(proof.name, NAME0); }); }); @@ -234,12 +235,13 @@ describe('Node http', function() { it('It should return a proof type of TYPE_DEADEND when an auction has not been initiated', async () => { const nameHash = rules.hashName(NAME0).toString('hex'); const proof = await nclient.get(`/proof/hash/${nameHash}`); - assert.equal(proof.proof.type, 'TYPE_DEADEND'); + assert.equal(proof.type, 'TYPE_DEADEND'); assert.equal(proof.name, null); }); describe('When an auction has been initiated', () => { it('It should return the name\'s proof', async () => { + const record = { compat: false, version: 0, ttl: 172800, ns: ['ns1.example.com.@1.2.3.4'] }; await mineBlocks(10, cbAddress); await wclient.execute('sendopen', [NAME0]); @@ -260,12 +262,12 @@ describe('Node http', function() { await mineBlocks(blocksUntilClose, cbAddress); - await wclient.execute('sendupdate', [NAME0, { compat: false, version: 0, ttl: 172800, ns: ['ns1.example.com.@1.2.3.4'] }]); + await wclient.execute('sendupdate', [NAME0, record]); await mineBlocks(1, cbAddress); const nameHash = rules.hashName(NAME0).toString('hex'); const proof = await nclient.get(`/proof/hash/${nameHash}`); - assert.equal(proof.proof.type, 'TYPE_EXISTS'); + assert.equal(proof.type, 'TYPE_EXISTS'); assert.equal(proof.name, NAME0); }); });