forked from crossbario/autobahn-js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial XBR protocol implementation (crossbario#424)
* dump current working tree * improvements * More seller code * Do some stuff on the network * More implementation... * bring things closer to Python impl * Call the market maker with right parameters * add preliminary xbr buyer code * Bring the XBR Seller near completion * Make improvements to the Buyer code * Bring buyer close to the finish line * Minor improvements here and there * Actually encrypt/decrypt secretbox * Move deferred factory to utils and reuse in XBR * Fix context * Nothing to resolve * dont export the private _onRotate * assign 'self' at a universal place
- Loading branch information
Showing
8 changed files
with
324 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
var cbor = require('cbor'); | ||
var nacl = require('tweetnacl'); | ||
var eth_accounts = require("web3-eth-accounts"); | ||
var eth_util = require("ethereumjs-util"); | ||
var util = require('../util.js'); | ||
|
||
|
||
var SimpleBuyer = function (buyerKey, maxPrice) { | ||
this._running = false; | ||
this._session = null; | ||
this._channel = null; | ||
this._balance = null; | ||
this._keys = {}; | ||
this._maxPrice = maxPrice; | ||
this._deferred_factory = util.deferred_factory(); | ||
|
||
var account = new eth_accounts.Accounts().privateKeyToAccount(buyerKey); | ||
this._addr = eth_util.toBuffer(account.address); | ||
|
||
this._keyPair = nacl.box.keyPair(); | ||
}; | ||
|
||
SimpleBuyer.prototype.start = function(session, consumerID) { | ||
self = this; | ||
self._session = session; | ||
self._running = true; | ||
|
||
var d = this._deferred_factory(); | ||
|
||
session.call('xbr.marketmaker.get_payment_channel', [self._addr]).then( | ||
function (paymentChannel) { | ||
self._channel = paymentChannel; | ||
self._balance = paymentChannel['remaining']; | ||
d.resolve(self._balance); | ||
}, | ||
function (error) { | ||
console.log("Call failed:", error); | ||
d.reject(error['error']); | ||
} | ||
); | ||
|
||
return util.promise(d); | ||
}; | ||
|
||
SimpleBuyer.prototype.stop = function () { | ||
this._running = false; | ||
}; | ||
|
||
SimpleBuyer.prototype.balance = function () { | ||
var d = this._deferred_factory(); | ||
this._session.call('xbr.marketmaker.get_payment_channel', [self._addr]).then( | ||
function (paymentChannel) { | ||
var balance = { | ||
amount: paymentChannel['amount'], | ||
remaining: paymentChannel['remaining'], | ||
inflight: paymentChannel['inflight'] | ||
}; | ||
d.resolve(balance); | ||
}, | ||
function (error) { | ||
console.log("Call failed:", error); | ||
d.reject(error['error']); | ||
} | ||
); | ||
return util.promise(d); | ||
}; | ||
|
||
SimpleBuyer.prototype.openChannel = function (buyerAddr, amount) { | ||
var signature = nacl.randomBytes(64); | ||
var d = this._deferred_factory(); | ||
this._session.call( | ||
'xbr.marketmaker.open_payment_channel', | ||
[buyerAddr, this._addr, amount, signature] | ||
).then( | ||
function (paymentChannel) { | ||
var balance = { | ||
amount: paymentChannel['amount'], | ||
remaining: paymentChannel['remaining'], | ||
inflight: paymentChannel['inflight'] | ||
}; | ||
d.resolve(balance); | ||
}, | ||
function (error) { | ||
console.log("Call failed:", error); | ||
d.reject(error['error']); | ||
} | ||
); | ||
return util.promise(d); | ||
}; | ||
|
||
SimpleBuyer.prototype.closeChannel = function () { | ||
}; | ||
|
||
SimpleBuyer.prototype.unwrap = function (keyID, ciphertext) { | ||
self = this; | ||
var d = self._deferred_factory(); | ||
if (!self._keys.hasOwnProperty(keyID)) { | ||
self._keys[keyID] = false; | ||
self._session.call( | ||
'xbr.marketmaker.buy_key', | ||
[self._addr, self._keyPair.publicKey, keyID, self._maxPrice, nacl.randomBytes(64)] | ||
).then( | ||
function (receipt) { | ||
var sealedKey = receipt['sealed_key']; | ||
try { | ||
self._keys[keyID] = nacl.sealedbox.open(sealedKey, self._keyPair.publicKey, | ||
self._keyPair.secretKey); | ||
var nonce = ciphertext.slice(0, nacl.secretbox.nonceLength); | ||
var message = ciphertext.slice(nacl.secretbox.nonceLength, ciphertext.length); | ||
var decrypted = nacl.secretbox.open(message, nonce, self._keys[keyID]); | ||
var payload = cbor.decode(decrypted); | ||
d.resolve(payload); | ||
} catch (e) { | ||
d.reject(e) | ||
} | ||
}, | ||
function (error) { | ||
d.reject(error['error']) | ||
} | ||
); | ||
} | ||
return util.promise(d); | ||
}; | ||
|
||
exports.SimpleBuyer = SimpleBuyer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
var cbor = require('cbor'); | ||
var nacl = require('tweetnacl'); | ||
var sealedbox = require('tweetnacl-sealedbox-js'); | ||
|
||
var KeySeries = function(apiID, prefix, price, interval, onRotate) { | ||
this.apiID = apiID; | ||
this.price = price; | ||
this.interval = interval; | ||
this.prefix = prefix; | ||
this.onRotate = onRotate; | ||
this._archive = {}; | ||
this._started = false; | ||
}; | ||
|
||
KeySeries.prototype.encrypt = function(payload) { | ||
var nonce = nacl.randomBytes(nacl.secretbox.nonceLength); | ||
var box = nacl.secretbox(cbor.encode(payload), nonce, this._archive[this.keyID]); | ||
var fullMessage = new Uint8Array(nonce.length + box.length); | ||
fullMessage.set(nonce); | ||
fullMessage.set(box, nonce.length); | ||
return fullMessage; | ||
}; | ||
|
||
KeySeries.prototype.encryptKey = function(keyID, buyerPubKey) { | ||
return sealedbox.seal(this._archive[this.keyID], buyerPubKey) | ||
}; | ||
|
||
KeySeries.prototype.start = function() { | ||
if (!this._started) { | ||
this._rotate(this); | ||
this._started = true; | ||
} | ||
}; | ||
|
||
KeySeries.prototype._rotate = function(context) { | ||
context.keyID = nacl.randomBytes(16); | ||
context._archive[context.keyID] = nacl.randomBytes(nacl.secretbox.keyLength); | ||
context.onRotate(context); | ||
// Rotate the keys | ||
// FIXME: make this to wait for the above onRotate callback to finish | ||
setTimeout(context._rotate, context.interval, context); | ||
}; | ||
|
||
KeySeries.prototype.stop = function() { | ||
if (this._started) { | ||
this._started = false; | ||
} | ||
}; | ||
|
||
exports.KeySeries = KeySeries; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
var autobahn = require("../autobahn.js"); | ||
var eth_accounts = require("web3-eth-accounts"); | ||
var eth_util = require("ethereumjs-util"); | ||
var key_series = require('./keyseries'); | ||
var util = require('../util.js'); | ||
|
||
|
||
var Seller = function (sellerKey) { | ||
self = this; | ||
this.sellerKey = sellerKey; | ||
this.keys = {}; | ||
this.keysMap = {}; | ||
this._providerID = eth_util.bufferToHex(eth_util.privateToPublic(sellerKey)); | ||
this._session = null; | ||
this.sessionRegs = []; | ||
this._deferred_factory = util.deferred_factory(); | ||
|
||
var account = new eth_accounts.Accounts().privateKeyToAccount(sellerKey); | ||
this._addr = eth_util.toBuffer(account.address); | ||
this._privateKey = eth_util.toBuffer(account.privateKey); | ||
}; | ||
|
||
Seller.prototype.start = function (session) { | ||
self._session = session; | ||
|
||
var d = this._deferred_factory(); | ||
var procedure = 'xbr.protocol.' + self._providerID + '.sell'; | ||
session.register(procedure, self.sell).then( | ||
function (registration) { | ||
self.sessionRegs.push(registration); | ||
for (var key in self.keys) { | ||
self.keys[key].start(); | ||
} | ||
d.resolve(); | ||
}, | ||
function (error) { | ||
console.log("Registration failed:", error); | ||
d.reject(); | ||
} | ||
); | ||
return util.promise(d); | ||
}; | ||
|
||
Seller.prototype.sell = function (key_id, buyer_pubkey) { | ||
if (!this.keysMap.hasOwnProperty(key_id)) { | ||
throw "no key with ID " + key_id; | ||
} | ||
return this.keysMap[key_id].encryptKey(key_id, buyer_pubkey) | ||
}; | ||
|
||
Seller.prototype.add = function (apiID, prefix, price, interval) { | ||
var keySeries = new key_series.KeySeries(apiID, prefix, price, interval, _onRotate); | ||
this.keys[apiID] = keySeries; | ||
return keySeries; | ||
}; | ||
|
||
var _onRotate = function (series) { | ||
self.keysMap[series.keyID] = series; | ||
|
||
self._session.call( | ||
'xbr.marketmaker.place_offer', | ||
[series.keyID, series.apiID, series.prefix, BigInt(Date.now() * 1000000 - 10 * 10 ** 9), | ||
self._addr, autobahn.nacl.randomBytes(64)], | ||
{price: series.price, provider_id: self._providerID} | ||
).then( | ||
function (result) { | ||
console.log("Offer placed for key", result['key']); | ||
}, | ||
function (error) { | ||
console.log("Call failed:", error); | ||
} | ||
) | ||
}; | ||
|
||
Seller.prototype.stop = function () { | ||
for (var key in this.keys) { | ||
this.keys[key].stop() | ||
} | ||
|
||
for (var i = 0; i < this.sessionRegs.length; i++) { | ||
this.sessionRegs[i].unregister() | ||
} | ||
}; | ||
|
||
Seller.prototype.wrap = function (api_id, uri, payload) { | ||
return this.keys[api_id].encrypt(payload) | ||
}; | ||
|
||
exports.SimpleSeller = Seller; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
exports.SimpleBuyer = require('./buyer.js').SimpleBuyer; | ||
exports.SimpleSeller = require('./seller.js').SimpleSeller; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters