diff --git a/README.md b/README.md index 1afe0cc3..8bf49a6f 100644 --- a/README.md +++ b/README.md @@ -238,7 +238,7 @@ For mutable content, the hash will be the hash of the public key, `opts.k`. These options are available: * `opts.k` - ed25519 public key buffer (32 bytes) (REQUIRED) -* `opts.sign(buf)` - function to generate an ed25519 signature buffer (64 bytes) corresponding to the `opts.k` public key (REQUIRED) +* `opts.sign(buf[, cb])` - function to generate an ed25519 signature buffer (64 bytes) corresponding to the `opts.k` public key (REQUIRED) * `opts.seq` - optional sequence (integer), must monotonically increase (REQUIRED) * `opts.cas` - optional previous sequence for compare-and-swap * `opts.salt` - optional salt buffer to include (<= 64 bytes) when calculating @@ -263,8 +263,8 @@ const opts = { k: keypair.pk, seq: 0, v: value, - sign: function (buf) { - return ed.sign(buf, keypair.sk) + sign: function (buf, cb) { + cb(ed.sign(buf, keypair.sk)) } } diff --git a/client.js b/client.js index 6bd28784..ea0c41e2 100644 --- a/client.js +++ b/client.js @@ -309,18 +309,33 @@ class DHT extends EventEmitter { if (opts.salt) message.a.salt = opts.salt message.a.k = opts.k message.a.seq = opts.seq - if (typeof opts.sign === 'function') message.a.sig = opts.sign(encodeSigData(message.a)) - else if (Buffer.isBuffer(opts.sig)) message.a.sig = opts.sig + if (typeof opts.sign === 'function') { + if (opts.sign.length === 1) { + message.a.sig = opts.sign(encodeSigData(message.a)) + this._putSigned(table, key, message, cb) + } else { + opts.sign(encodeSigData(message.a), (sig) => { + message.a.sig = sig + this._putSigned(table, key, message, cb) + }) + } + } else if (Buffer.isBuffer(opts.sig)) { + message.a.sig = opts.sig + this._putSigned(table, key, message, cb) + } } else { this._values.set(key.toString('hex'), message.a) + this._putSigned(table, key, message, cb) } + return key + } + + _putSigned (table, key, message, cb) { this._rpc.queryAll(table.closest(key), message, null, (err, n) => { if (err) return cb(err, key, n) cb(null, key, n) }) - - return key } _preput (key, opts, cb) { diff --git a/test/dht_store_mutable.js b/test/dht_store_mutable.js index 2fd72177..ec971419 100644 --- a/test/dht_store_mutable.js +++ b/test/dht_store_mutable.js @@ -612,3 +612,47 @@ test('valid sequence', t => { } }) }) + +test('asynchronous signing', t => { + t.plan(4) + + const keypair = ed.keygen() + + const dht = new DHT({ bootstrap: false, verify: ed.verify }) + t.once('end', () => { + dht.destroy() + }) + common.failOnWarningOrError(t, dht) + + dht.listen(() => { + dht.addNode({ host: '127.0.0.1', port: dht.address().port }) + dht.once('node', ready) + }) + + function ready () { + const value = common.fill(500, 'abc') + const opts = { + k: keypair.pk, + sign: (data, cb) => cb(common.sign(keypair)(data)), + seq: 0, + v: value + } + + const expectedHash = crypto.createHash('sha1').update(opts.k).digest() + + dht.put(opts, (_, hash) => { + t.equal( + hash.toString('hex'), + expectedHash.toString('hex'), + 'hash of the public key' + ) + dht.get(hash, (err, res) => { + t.ifError(err) + t.equal(res.v.toString('utf8'), opts.v.toString('utf8'), + 'got back what we put in' + ) + t.equal(res.seq, 0) + }) + }) + } +})