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

Test JS to Go connections with both RSA and Secp256k1 keys #21

Merged
merged 3 commits into from
Jul 15, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@
},
"homepage": "https://github.com/libp2p/interop#readme",
"devDependencies": {
"aegir": "^19.0.3",
"libp2p-daemon": "^0.2.2",
"aegir": "^19.0.5",
"chai": "^4.2.0",
"chai-bytes": "~0.1.2",
"chai-checkmark": "^1.0.1",
"cross-env": "^5.2.0",
"dirty-chai": "^2.0.1",
"go-libp2p-dep": "~0.1.0",
"libp2p-daemon": "~0.2.1",
"libp2p-daemon-client": "~0.2.1",
"multiaddr": "^6.0.6",
"rimraf": "^2.6.3"
Expand Down
21 changes: 14 additions & 7 deletions src/daemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,29 +100,36 @@ class Daemon {
_startDaemon (options) {
return new Promise((resolve, reject) => {
let execOptions
const addr = this._addr.toString()

// TODO refactor this once we daemon supports a json config
if (this._type === 'go') {
execOptions = ['-listen', this._addr]
execOptions = ['-listen', addr]

options.dht && execOptions.push('-dht')
options.pubsub && execOptions.push('-pubsub')
} else {
execOptions = ['--listen', this._addr]
execOptions = ['--listen', addr]

options.dht && execOptions.push('--dht')
options.pubsub && execOptions.push('--pubsub')
}
if ((options.keyFile || '') !== '') {
execOptions.push(`--id=${options.keyFile}`)
}

const daemon = execa(this._binPath, execOptions)

daemon.stdout.once('data', () => {
return resolve()
resolve()
})

daemon.stderr.on('data', (data) => {
if (!data.toString().includes('Warning')) {
return reject(data.toString())
daemon.on('exit', (code, signal) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I figure it's more robust to fail if the daemon fails, instead of when it simply prints a warning?

Copy link
Member

Choose a reason for hiding this comment

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

I agree with that!

if (code !== 0) {
reject(new Error(`daemon exited with status code ${code}`))
} else if ((signal || '') !== '') {
reject(new Error(`daemon exited due to signal ${signal}`))
} else {
resolve()
}
})
})
Expand Down
102 changes: 71 additions & 31 deletions test/connect/js2go.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,86 @@ const expect = chai.expect

const spawnDaemons = require('../utils/spawnDaemons')

describe('connect', () => {
let daemons
const beforeConnect = (ctx, keyType) => {
ctx.timeout(20 * 1000)

// Start Daemons
before(async function () {
this.timeout(20 * 1000)
return spawnDaemons(2, [{ type: 'js', keyType }, { type: 'go', keyType }])
}

daemons = await spawnDaemons(2, ['js', 'go'])
})
const afterConnect = async (daemons) => {
if (daemons == null) {
return
}

// Stop daemons
after(async function () {
await Promise.all(
daemons.map((daemon) => daemon.stop())
)
})
await Promise.all(
daemons.map(async (daemon) => {
// Ignore errors
try {
await daemon.stop()
} catch (_) {
}
})
)
}

const performTest = async (ctx, daemons) => {
ctx.timeout(10 * 1000)

const identifyJs = await daemons[0].client.identify()
const jsId = identifyJs.peerId.toB58String()
const identifyGo = await daemons[1].client.identify()
const goId = identifyGo.peerId.toB58String()

// verify connected peers
const knownPeersBeforeConnectJs = await daemons[0].client.listPeers()
expect(knownPeersBeforeConnectJs).to.have.lengthOf(0)

const knownPeersBeforeConnectGo = await daemons[1].client.listPeers()
expect(knownPeersBeforeConnectGo).to.have.lengthOf(0)

it('js peer to go peer', async function () {
this.timeout(10 * 1000)
// connect peers
await daemons[0].client.connect(identifyGo.peerId, identifyGo.addrs)

const identifyJs = await daemons[0].client.identify()
const identifyGo = await daemons[1].client.identify()
// verify connected peers
const knownPeersAfterConnectJs = await daemons[0].client.listPeers()
expect(knownPeersAfterConnectJs).to.have.lengthOf(1)
expect(knownPeersAfterConnectJs[0].toB58String()).to.equal(goId)

// verify connected peers
const knownPeersBeforeConnectJs = await daemons[0].client.listPeers()
expect(knownPeersBeforeConnectJs).to.have.lengthOf(0)
const knownPeersAfterConnectGo = await daemons[1].client.listPeers()
expect(knownPeersAfterConnectGo).to.have.lengthOf(1)
expect(knownPeersAfterConnectGo[0].toB58String()).to.equal(jsId)
}

describe('connecting js peer to go peer', () => {
describe('with RSA keys', () => {
let daemons

before(async function () {
daemons = await beforeConnect(this, 'rsa')
})

after(async () => {
await afterConnect(daemons)
})

it('should work', async function () {
await performTest(this, daemons)
})
})

const knownPeersBeforeConnectGo = await daemons[1].client.listPeers()
expect(knownPeersBeforeConnectGo).to.have.lengthOf(0)
describe('with SECP256k1 keys', () => {
let daemons

// connect peers
await daemons[0].client.connect(identifyGo.peerId, identifyGo.addrs)
before(async function () {
daemons = await beforeConnect(this, 'secp256k1')
})

// verify connected peers
const knownPeersAfterConnectJs = await daemons[0].client.listPeers()
expect(knownPeersAfterConnectJs).to.have.lengthOf(1)
expect(knownPeersAfterConnectJs[0].toB58String()).to.equal(identifyGo.peerId.toB58String())
after(async () => {
await afterConnect(daemons)
})

const knownPeersAfterConnectGo = await daemons[1].client.listPeers()
expect(knownPeersAfterConnectGo).to.have.lengthOf(1)
expect(knownPeersAfterConnectGo[0].toB58String()).to.equal(identifyJs.peerId.toB58String())
it('should work', async function () {
await performTest(this, daemons)
})
})
})
Binary file added test/resources/keys/go.rsa.key
Binary file not shown.
1 change: 1 addition & 0 deletions test/resources/keys/go.secp256k1.key
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
 {��ܥg� oT+.�\�L� Y��Jb���G�q7�
Binary file added test/resources/keys/js.rsa.key
Binary file not shown.
1 change: 1 addition & 0 deletions test/resources/keys/js.secp256k1.key
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
 �z�m�/�kPa�ࡓ$�Z���p"�>v��
55 changes: 31 additions & 24 deletions test/utils/spawnDaemons.js
Original file line number Diff line number Diff line change
@@ -1,58 +1,65 @@
'use strict'

const assert = require('assert')
const path = require('path')

const Daemon = require('../../src/daemon')

const startPortNumber = 9000

/**
* @param {number} n number of nodes to spawn
* @param {string|array} type nodes type (default: js)
* @param {string|array} specs node specs (default: 'js')
* @param {Object|array} options daemon options
*/
async function spawnDaemons (n, type = 'js', options) {
async function spawnDaemons (n, specs = 'js', options) {
assert(n, 'spawnDaemons require a number of nodes to start')
assert(validType(n, type), 'spawnDaemons type is not valid')

let types = type

if (!Array.isArray(types)) {
types = new Array(n).fill(type)
if (!Array.isArray(specs)) {
specs = new Array(n).fill(specs)
}
specs = specs.map((spec) => {
if (typeof spec === 'string') {
return {
type: spec
}
}

let daemonOptions = options
return spec
})
validateSpecs(n, specs)

let daemonOptions = options
if (!Array.isArray(daemonOptions)) {
daemonOptions = new Array(n).fill(options)
}

const daemons = []
let daemon

for (let i = 0; i < n; i++) {
daemon = new Daemon(types[i], `/tmp/p2pd-${i}.sock`, startPortNumber + i)
const spec = specs[i]
const daemon = new Daemon(spec.type, `/tmp/p2pd-${i}.sock`, startPortNumber + i)
daemons.push(daemon)
}

await Promise.all(daemons.map((daemon, i) => daemon.start(daemonOptions[i])))
await Promise.all(daemons.map((daemon, i) => {
const spec = specs[i]
const opts = daemonOptions[i] || {}
opts.keyFile = spec.keyType != null
? path.resolve(__dirname, `../resources/keys/${spec.type}.${spec.keyType}.key`) : null
return daemon.start(opts)
}))

return daemons
}

function validType (n, type) {
// validate string type
if (typeof type === 'string' && (type === 'js' || type === 'go')) {
return true
}

// validate array of types
if (Array.isArray(type) && type.length === n &&
!type.filter((t) => (t !== 'go' && t !== 'js')).length) {
return true
}
function validateSpecs (n, specs) {
assert(specs.length === n, 'number of specs must be equal to n')

return false
specs.forEach((spec) => {
assert(spec.type === 'js' || spec.type === 'go', `invalid spec type ${spec.type}`)
aknuds1 marked this conversation as resolved.
Show resolved Hide resolved
assert(spec.keyType == null || spec.keyType === 'rsa' || spec.keyType === 'secp256k1',
aknuds1 marked this conversation as resolved.
Show resolved Hide resolved
`invalid spec key type ${spec.keyType}`)
})
}

module.exports = spawnDaemons