Merge pull request #173 from matiu/feature/peer-authentication

Feature/peer authentication
This commit is contained in:
Ryan X. Charles 2014-04-24 12:35:58 -03:00
commit 486685f757
9 changed files with 280 additions and 156 deletions

View File

@ -57,16 +57,30 @@
</div> </div>
<div class="row"> <div class="row">
<div ng-if='$root.wallet && !$root.wallet.publicKeyRing.isComplete() && !loading' data-alert class="alert-box info round" > <div ng-if='$root.wallet && !$root.wallet.publicKeyRing.isComplete() && !loading' data-alert class="alert-box warn round size-18" >
<i class="fi-alert size-18"></i>
Note: Your wallet is not complete yet.
<span ng-show="$root.wallet.publicKeyRing.totalCopayers - $root.wallet.publicKeyRing.registeredCopayers()>1"> <div style="float:left; margin:0 15px">
{{$root.wallet.publicKeyRing.totalCopayers - $root.wallet.publicKeyRing.registeredCopayers() }} keys are <i class="fi-alert size-60"></i>
</span> </div>
<span ng-show="$root.wallet.publicKeyRing.totalCopayers - $root.wallet.publicKeyRing.registeredCopayers()==1">
One key is <div class="size-18">
</span> Note: Your wallet is not complete yet.
missing. Share this secret with your other copayers for them to join your wallet: <b>{{$root.wallet.getMyPeerId()}}</b> <span ng-show="$root.wallet.publicKeyRing.totalCopayers - $root.wallet.publicKeyRing.registeredCopayers()>1">
{{$root.wallet.publicKeyRing.totalCopayers - $root.wallet.publicKeyRing.registeredCopayers() }} keys are
</span>
<span ng-show="$root.wallet.publicKeyRing.totalCopayers - $root.wallet.publicKeyRing.registeredCopayers()==1">
One key is
</span>
missing.
</div>
<div class="size-18">
Share this secret with your other copayers for them to join your wallet <br>
</div>
<div class="size-24">
<b>{{$root.wallet.getMyCopayerId()}}</b>
</div>
</div> </div>
</div> </div>
@ -115,7 +129,7 @@
<div class="large-6 columns"> <div class="large-6 columns">
<h3>Join a Wallet in Creation</h3> <h3>Join a Wallet in Creation</h3>
<input type="text" class="form-control" placeholder="Paste wallet secret here" <input type="text" class="form-control" placeholder="Paste wallet secret here"
ng-model="connectionId" required ng-minlength="32" ng-maxlength="32" autofocus> ng-model="connectionId" required autofocus>
</div> </div>
<div class="large-3 columns"> <div class="large-3 columns">
<button class="button primary expand round" <button class="button primary expand round"
@ -179,28 +193,17 @@
<script type="text/ng-template" id="peer.html"> <script type="text/ng-template" id="peer.html">
<div class="row"> <div class="row">
<div class="large-6 columns p70r line-dashed-v"> <div class="large-12 columns p70l">
<h3>I am </h3> <h3 class="panel-title">Online Copayers: {{$root.wallet.network.connectedCopayers().length}}</h3>
<div class="row">
<div class="large-10 columns p0r">
<p class="panel share-wallet">{{$root.wallet.network.peerId}}</p>
</div>
<div class="large-2 columns">
<p class="panel panel-sign"> <i class="fi-page-copy size-22"></i> </p>
</div>
</div>
</div>
<div class="large-6 columns p70l">
<h3 class="panel-title">Online Copayers: {{$root.wallet.network.connectedPeers.length}}</h3>
<p class="text-info" ng-show="$root.wallet.publicKeyRing.requiredCopayers > $root.wallet.network.connectedPeers.length"> <i class="fi-alert size-28"></i> <p class="text-info" ng-show="$root.wallet.publicKeyRing.requiredCopayers >$root.wallet.network.connectedCopayers()"> <i class="fi-alert size-28"></i>
{{$root.wallet.publicKeyRing.requiredCopayers}} copayers needed for signing transactions {{$root.wallet.publicKeyRing.requiredCopayers}} copayers needed for signing transactions
<ul class="no-bullet"> <ul class="no-bullet">
<li class="panel" ng-repeat="copayer in $root.wallet.network.connectedPeers"> <li class="panel" ng-repeat="copayer in $root.wallet.network.connectedCopayers()">
<span ng-if="copayer == $root.wallet.network.peerId"> You ({{copayer}})</span> <span ng-if="copayer === $root.wallet.getMyCopayerId()"> You </span>
<span ng-if="copayer !== $root.wallet.network.peerId">{{copayer}}</span> {{copayer}}
<span> <span>
<i class="fi-check size-16 panel-sign right p5h br100"></i> <i class="fi-check size-16 panel-sign right p5h br100"></i>
</span> </span>
@ -276,9 +279,8 @@
<tr ng-repeat="(peer, actions) in tx.peerActions"> <tr ng-repeat="(peer, actions) in tx.peerActions">
<td colspan="3"> <td colspan="3">
<div class="panel status"> <div class="panel status">
<span ng-if="peer == $root.wallet.network.peerId"> You <span ng-if="peer === $root.wallet.getMyCopayerId()"> You</span>
</span> {{peer}}
<span ng-if="peer != $root.wallet.network.peerId"> {{peer}}</span>
<span ng-repeat="(action, ts) in actions"> <span ng-repeat="(action, ts) in actions">
<span ng-if="action == 'create'" title="{{ts | date:'medium'}}" class="fi-page-add size-18 right m10h"></span> <span ng-if="action == 'create'" title="{{ts | date:'medium'}}" class="fi-page-add size-18 right m10h"></span>

View File

@ -59,8 +59,8 @@ angular.module('copay.transactions').controller('TransactionsController',
$rootScope.$digest(); $rootScope.$digest();
return; return;
} }
var p = w.getTxProposal(ntxid); var p = w.txProposals.getTxProposal(ntxid);
if (p.txp.builder.isFullySigned()) { if (p.builder.isFullySigned()) {
$scope.send(ntxid); $scope.send(ntxid);
_updateTxs(); _updateTxs();
$rootScope.$digest(); $rootScope.$digest();

View File

@ -18,15 +18,23 @@ function PrivateKey(opts) {
this.privateKeyCache = opts.privateKeyCache || {}; this.privateKeyCache = opts.privateKeyCache || {};
}; };
PrivateKey.prototype.getId = function(prefix) { PrivateKey.prototype.getId = function() {
var buf = this.bip.extendedPublicKey; if (!this.id) {
if (prefix) { var path = PublicKeyRing.SIGNING_BRANCH;
buf = Buffer.concat([prefix, buf]); var bip32 = this.bip.derive(path);
this.id= bip32.eckey.public.toString('hex');
} }
var hash = util.sha256(buf).toString('hex'); return this.id;
return hash.substring(0, hash.length/2);
}; };
PrivateKey.prototype.getSigningKey = function() {
if (!this.sid) {
var path = PublicKeyRing.SIGNING_BRANCH;
var bip32 = this.bip.derive(path);
this.sid= bip32.eckey.private.toString('hex');
}
return this.sid;
};
PrivateKey.fromObj = function(obj) { PrivateKey.fromObj = function(obj) {
return new PrivateKey(obj); return new PrivateKey(obj);

View File

@ -44,9 +44,12 @@ function PublicKeyRing(opts) {
*/ */
PublicKeyRing.Branch = function (index, isChange) { PublicKeyRing.Branch = function (index, isChange) {
return 'm/'+(isChange?1:0)+'/'+index; // first 0 is for future use: could be copayerId.
return 'm/0/'+(isChange?1:0)+'/'+index;
}; };
PublicKeyRing.SIGNING_BRANCH = 'm/100/0/0';
PublicKeyRing.fromObj = function (data) { PublicKeyRing.fromObj = function (data) {
if (data instanceof PublicKeyRing) { if (data instanceof PublicKeyRing) {
throw new Error('bad data format: Did you use .toObj()?'); throw new Error('bad data format: Did you use .toObj()?');
@ -77,17 +80,20 @@ PublicKeyRing.prototype.serialize = function () {
return JSON.stringify(this.toObj()); return JSON.stringify(this.toObj());
}; };
PublicKeyRing.prototype.getCopayerId = function(i, prefix) { PublicKeyRing.prototype.getCopayerId = function(i) {
var buf = this.copayersBIP32[i].extendedPublicKey; this.copayerIds = this.copayerIds || [];
if (prefix) {
buf = Buffer.concat([prefix, buf]); if (!this.copayerIds[i]) {
var path = PublicKeyRing.SIGNING_BRANCH;
var bip32 = this.copayersBIP32[i].derive(path);
this.copayerIds[i]= bip32.eckey.public.toString('hex');
} }
var hash = util.sha256(buf).toString('hex');
return hash.substring(0, hash.length/2); return this.copayerIds[i];
}; };
PublicKeyRing.prototype.myCopayerId = function(i, prefix) { PublicKeyRing.prototype.myCopayerId = function(i) {
return this.getCopayerId(0,prefix); return this.getCopayerId(0);
}; };
PublicKeyRing.prototype.registeredCopayers = function () { PublicKeyRing.prototype.registeredCopayers = function () {

View File

@ -122,10 +122,10 @@ Wallet.prototype._handleData = function(senderId, data, isInbound) {
} }
}; };
Wallet.prototype._handleNetworkChange = function(newPeerId) { Wallet.prototype._handleNetworkChange = function(newCopayerId) {
if (newPeerId) { if (newCopayerId) {
this.log('#### Setting new PEER:', newPeerId); this.log('#### Setting new PEER:', newCopayerId);
this.sendWalletId(newPeerId); this.sendWalletId(newCopayerId);
} }
this.emit('refresh'); this.emit('refresh');
}; };
@ -143,13 +143,13 @@ Wallet.prototype._optsToObj = function () {
}; };
Wallet.prototype.getPeerId = function(index) { Wallet.prototype.getCopayerId = function(index) {
return this.publicKeyRing.getCopayerId(index || 0); return this.publicKeyRing.getCopayerId(index || 0);
}; };
Wallet.prototype.getMyPeerId = function() { Wallet.prototype.getMyCopayerId = function() {
return this.getPeerId(0); return this.getCopayerId(0);
}; };
Wallet.prototype.netStart = function() { Wallet.prototype.netStart = function() {
@ -167,22 +167,25 @@ Wallet.prototype.netStart = function() {
self.emit('close'); self.emit('close');
}); });
var myPeerId = self.getMyPeerId(); var myId = self.getMyCopayerId();
var startOpts = { var startOpts = {
peerId: myPeerId copayerId: myId,
signingKeyHex: self.privateKey.getSigningKey(),
}; };
net.start(function() { net.start(startOpts, function() {
self.emit('created'); self.emit('created');
for (var i=0; i<self.publicKeyRing.registeredCopayers(); i++) { for (var i=0; i<self.publicKeyRing.registeredCopayers(); i++) {
var otherPeerId = self.getPeerId(i); var otherId = self.getCopayerId(i);
if (otherPeerId !== myPeerId) { if (otherId !== myId) {
net.connectTo(otherPeerId); net.connectTo(otherId);
} }
self.sendWalletReady(self.firstPeerId); if (self.firstCopayerId){
self.firstPeerId = null; self.sendWalletReady(self.firstCopayerId);
self.firstCopayerId = null;
}
self.emit('refresh'); self.emit('refresh');
} }
}, startOpts); });
}; };
Wallet.prototype.store = function(isSync) { Wallet.prototype.store = function(isSync) {
@ -279,8 +282,8 @@ Wallet.prototype.getTxProposals = function() {
var ret = []; var ret = [];
for(var k in this.txProposals.txps) { for(var k in this.txProposals.txps) {
var i = this.txProposals.getTxProposal(k); var i = this.txProposals.getTxProposal(k);
i.signedByUs = i.signedBy[this.getMyPeerId()]?true:false; i.signedByUs = i.signedBy[this.getMyCopayerId()]?true:false;
i.rejectedByUs = i.rejectedBy[this.getMyPeerId()]?true:false; i.rejectedByUs = i.rejectedBy[this.getMyCopayerId()]?true:false;
if (this.totalCopayers-i.rejectCount < this.requiredCopayers) if (this.totalCopayers-i.rejectCount < this.requiredCopayers)
i.finallyRejected=true; i.finallyRejected=true;
@ -291,7 +294,7 @@ Wallet.prototype.getTxProposals = function() {
Wallet.prototype.reject = function(ntxid) { Wallet.prototype.reject = function(ntxid) {
var myId=this.getMyPeerId(); var myId=this.getMyCopayerId();
var txp = this.txProposals.txps[ntxid]; var txp = this.txProposals.txps[ntxid];
if (!txp || txp.rejectedBy[myId] || txp.signedBy[myId]) return; if (!txp || txp.rejectedBy[myId] || txp.signedBy[myId]) return;
@ -303,7 +306,7 @@ Wallet.prototype.reject = function(ntxid) {
Wallet.prototype.sign = function(ntxid) { Wallet.prototype.sign = function(ntxid) {
var self = this; var self = this;
var myId=this.getMyPeerId(); var myId=this.getMyCopayerId();
var txp = self.txProposals.txps[ntxid]; var txp = self.txProposals.txps[ntxid];
if (!txp || txp.rejectedBy[myId] || txp.signedBy[myId]) return; if (!txp || txp.rejectedBy[myId] || txp.signedBy[myId]) return;
@ -350,7 +353,7 @@ Wallet.prototype.sendTx = function(ntxid, cb) {
Wallet.prototype.addSeenToTxProposals = function() { Wallet.prototype.addSeenToTxProposals = function() {
var ret=false; var ret=false;
var myId=this.getMyPeerId(); var myId=this.getMyCopayerId();
for(var k in this.txProposals.txps) { for(var k in this.txProposals.txps) {
var txp = this.txProposals.txps[k]; var txp = this.txProposals.txps[k];
@ -489,7 +492,7 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, utxos, opts) {
if (priv) { if (priv) {
b.sign( priv.getAll(pkr.addressIndex, pkr.changeAddressIndex) ); b.sign( priv.getAll(pkr.addressIndex, pkr.changeAddressIndex) );
} }
var myId = this.getMyPeerId(); var myId = this.getMyCopayerId();
var now = Date.now(); var now = Date.now();
var me = {}; var me = {};

View File

@ -139,21 +139,21 @@ WalletFactory.prototype.remove = function(walletId) {
}; };
WalletFactory.prototype.joinCreateSession = function(peerId, cb) { WalletFactory.prototype.joinCreateSession = function(copayerId, cb) {
var self = this; var self = this;
//Create our PrivateK //Create our PrivateK
var privateKey = new PrivateKey({ networkName: this.networkName }); var privateKey = new PrivateKey({ networkName: this.networkName });
this.log('\t### PrivateKey Initialized'); this.log('\t### PrivateKey Initialized');
self.network.setPeerId(privateKey.getId()); self.network.setCopayerId(privateKey.getId());
self.network.setSigningKey(privateKey.getSigningKey());
self.network.start(function() { self.network.start({}, function() {
self.network.connectTo(peerId); self.network.connectTo(copayerId);
self.network.on('data', function(sender, data) { self.network.on('data', function(sender, data) {
if (data.type ==='walletId') { if (data.type ==='walletId') {
data.opts.privateKey = privateKey; data.opts.privateKey = privateKey;
var w = self.open(data.walletId, data.opts); var w = self.open(data.walletId, data.opts);
w.firstPeerId = peerId; w.firstCopayerId = copayerId;
return cb(w); return cb(w);
} }
}); });

View File

@ -1,7 +1,9 @@
var imports = require('soop').imports(); var imports = require('soop').imports();
var EventEmitter= imports.EventEmitter || require('events').EventEmitter; var EventEmitter= imports.EventEmitter || require('events').EventEmitter;
var bitcore = require('bitcore');
var util = bitcore.util;
var Key = bitcore.Key;
/* /*
* Emits * Emits
* 'networkChange' * 'networkChange'
@ -11,7 +13,6 @@ var EventEmitter= imports.EventEmitter || require('events').EventEmitter;
* when an unknown data type arrives * when an unknown data type arrives
* *
* Provides * Provides
* send(toPeerIds, {data}, cb?)
* *
*/ */
@ -24,6 +25,7 @@ function Network(opts) {
this.maxPeers = opts.maxPeers || 10; this.maxPeers = opts.maxPeers || 10;
this.opts = { key: opts.key }; this.opts = { key: opts.key };
this.connections = {}; this.connections = {};
this.copayerForPeer = {};
// For using your own peerJs server // For using your own peerJs server
['port', 'host', 'path', 'debug'].forEach(function(k) { ['port', 'host', 'path', 'debug'].forEach(function(k) {
@ -69,70 +71,123 @@ Network._arrayRemove = function(el, array) {
return array; return array;
}; };
Network.prototype._onClose = function(peerId) {
console.log('[WebRTC.js.72:_onClose:]');
Network.prototype.connectedCopayers = function() {
var ret =[];
for(var i in this.connectedPeers){
var copayerId =this.copayerForPeer[this.connectedPeers[i]];
if (copayerId) ret.push(copayerId);
}
return ret;
};
Network.prototype._onClose = function(peerId) {
delete this.connections[peerId]; delete this.connections[peerId];
this.connectedPeers = Network._arrayRemove(peerId, this.connectedPeers); this.connectedPeers = Network._arrayRemove(peerId, this.connectedPeers);
this._notifyNetworkChange(); this._notifyNetworkChange();
}; };
Network.prototype._connectToPeers = function(peerIds) { Network.prototype._connectToCopayers = function(copayerIds) {
var self = this; var self = this;
var ret = false; var arrayDiff= Network._arrayDiff(copayerIds, this.connectedCopayers());
var arrayDiff1= Network._arrayDiff(peerIds, this.connectedPeers); arrayDiff.forEach(function(copayerId) {
var arrayDiff = Network._arrayDiff(arrayDiff1, [this.peerId]); console.log('### CONNECTING TO:', copayerId);
arrayDiff.forEach(function(peerId) { self.connectTo(copayerId);
console.log('### CONNECTING TO:', peerId);
self.connectTo(peerId);
ret = true;
}); });
return ret;
}; };
Network.prototype._onData = function(data, isInbound) { Network.prototype._sendHello = function(copayerId) {
var obj; console.log('#### SENDING HELLO TO ', copayerId);
this.send(copayerId, {
type: 'hello',
copayerId: this.copayerId,
});
};
Network.prototype._sendCopayers = function(copayerIds) {
console.log('#### SENDING PEER LIST: ', this.connectedPeers,this.connectedCopayers(), ' TO ', copayerIds?copayerIds: 'ALL');
this.send(copayerIds, {
type: 'copayers',
copayers: this.connectedCopayers(),
});
};
Network.prototype._addCopayer = function(copayerId, isInbound) {
var peerId = this.peerFromCopayer(copayerId);
this._addCopayerMap(peerId,copayerId);
var hasChanged = Network._arrayPushOnce(peerId, this.connectedPeers);
if (isInbound && hasChanged) {
this._sendCopayers(); //broadcast peer list
}
else {
if (isInbound) {
this._sendCopayers(copayerId);
}
}
};
Network.prototype._onData = function(data, isInbound, peerId) {
var sig, payload;
try { try {
obj = JSON.parse(data); var dataObj = JSON.parse(data);
sig = dataObj.sig;
payload= dataObj.payload;
} catch (e) { } catch (e) {
console.log('### ERROR ON DATA: "%s" ', data, isInbound, e); console.log('### ERROR ON DATA: "%s" ', data, isInbound, e);
return; return;
}; };
console.log('### RECEIVED TYPE: %s FROM %s', obj.data.type, obj.sender, obj.data);
switch(obj.data.type) { console.log('### RECEIVED INBOUND?:%s TYPE: %s FROM %s: sig:%s',
case 'peerList': isInbound, payload.type, peerId, sig, payload);
this._connectToPeers(obj.data.peers); var self=this;
// TODO _func
if(payload.type === 'hello') {
var thisSig = this._sign(payload, this.copayerId);
if (thisSig !== sig) {
console.log('#### Peer sent WRONG hello. Closing connection.');
return;
}
console.log('#### Peer sent signed hello. Setting it up.'); //TODO
this._addCopayer(payload.copayerId, isInbound);
this._notifyNetworkChange( isInbound ? payload.copayerId : null);
this.emit('open');
return;
}
if (!this.copayerForPeer[peerId]) {
console.log('### Discarting message from unknow peer: ', peerId); //TODO
return;
}
// check sig
if (this.copayerForPeer[peerId]) {
var copayerIdBuf = new Buffer(this.copayerForPeer[peerId],'hex');
if (!bitcore.Message.verifyWithPubKey( copayerIdBuf, JSON.stringify(payload),
new Buffer(sig,'hex'))) {
console.log('[WebRTC.js.152] SIGNATURE VERIFICATION FAILED!!'); //TODO
// TODO close connection
return;
}
}
switch(payload.type) {
case 'copayers':
this._addCopayer(this.copayerForPeer[peerId], false);
this._connectToCopayers(payload.copayers);
this._notifyNetworkChange(); this._notifyNetworkChange();
break; break;
case 'disconnect': case 'disconnect':
this._onClose(obj.sender); this._onClose(peerId);
break; break;
default: default:
this.emit('data', obj.sender, obj.data, isInbound); this.emit('data', self.copayerForPeer[peerId], payload, isInbound);
} }
}; };
Network.prototype._sendPeers = function(peerIds) {
console.log('#### SENDING PEER LIST: ', this.connectedPeers, ' TO ', peerIds?peerIds: 'ALL');
this.send(peerIds, {
type: 'peerList',
peers: this.connectedPeers,
});
};
Network.prototype._addPeer = function(peerId, isInbound) {
var hasChanged = Network._arrayPushOnce(peerId, this.connectedPeers);
if (isInbound && hasChanged) {
this._sendPeers(); //broadcast peer list
}
else {
if (isInbound) {
this._sendPeers(peerId);
}
}
};
Network.prototype._checkAnyPeer = function() { Network.prototype._checkAnyPeer = function() {
if (!this.connectedPeers.length) { if (!this.connectedPeers.length) {
@ -145,20 +200,22 @@ Network.prototype._setupConnectionHandlers = function(dataConn, isInbound) {
var self=this; var self=this;
dataConn.on('open', function() { dataConn.on('open', function() {
if (!Network._inArray(dataConn.peer, self.connectedPeers)) { if (!Network._inArray(dataConn.peer, self.connectedPeers)
&& ! self.connections[dataConn.peer]) {
self.connections[dataConn.peer] = dataConn; self.connections[dataConn.peer] = dataConn;
console.log('### DATA CONNECTION READY TO: ADDING PEER: %s (inbound: %s)', console.log('### DATA CONNECTION READY: %s (inbound: %s) AUTHENTICATING...',
dataConn.peer, isInbound); dataConn.peer, isInbound);
self._addPeer(dataConn.peer, isInbound); // The connection peer send hello (with signature)
self._notifyNetworkChange( isInbound ? dataConn.peer : null); if(!isInbound)
this.emit('open'); self._sendHello(self.copayerForPeer[dataConn.peer]);
} }
}); });
dataConn.on('data', function(data) { dataConn.on('data', function(data) {
self._onData(data, isInbound); self._onData(data, isInbound, dataConn.peer);
}); });
dataConn.on('error', function(e) { dataConn.on('error', function(e) {
@ -177,9 +234,9 @@ Network.prototype._setupConnectionHandlers = function(dataConn, isInbound) {
}); });
}; };
Network.prototype._notifyNetworkChange = function(newPeer) { Network.prototype._notifyNetworkChange = function(newCopayerId) {
console.log('[WebRTC.js.164:_notifyNetworkChange:]', newPeer); //TODO console.log('[WebRTC.js.164:_notifyNetworkChange:]', newCopayerId); //TODO
this.emit('networkChange', newPeer); this.emit('networkChange', newCopayerId);
}; };
Network.prototype._setupPeerHandlers = function(openCallback) { Network.prototype._setupPeerHandlers = function(openCallback) {
@ -188,6 +245,8 @@ Network.prototype._setupPeerHandlers = function(openCallback) {
p.on('open', function() { p.on('open', function() {
self.connectedPeers = [self.peerId]; self.connectedPeers = [self.peerId];
self.copayerForPeer[self.peerId]= self.copayerId;
return openCallback(); return openCallback();
}); });
@ -213,23 +272,49 @@ Network.prototype._setupPeerHandlers = function(openCallback) {
}); });
}; };
Network.prototype.setPeerId = function(peerId) {
Network.prototype._addCopayerMap = function(peerId, copayerId) {
if (!this.copayerForPeer[peerId]) {
console.log('ADDING COPAYER MAPPING: %s => %s', peerId, copayerId); //TODO
this.copayerForPeer[peerId]=copayerId;
}
};
Network.prototype.setCopayerId = function(copayerId) {
if (this.started) { if (this.started) {
throw new Error ('network already started: can not change peerId') throw new Error ('network already started: can not change peerId')
} }
this.peerId = peerId; this.copayerId = copayerId;
this.copayerIdBuf = new Buffer(copayerId,'hex');
this.peerId = this.peerFromCopayer(this.copayerId);
this._addCopayerMap(this.peerId,copayerId);
}; };
Network.prototype.start = function(openCallback, opts) { Network.prototype.setSigningKey = function(keyHex) {
if (this.started || this.signingKey) {
throw new Error ('network already started or key assigned: can not change key')
}
var k = new Key();
k.private = new Buffer(keyHex,'hex');
k.regenerateSync();
this.signingKey = k;
};
Network.prototype.peerFromCopayer = function(hex) {
return util.sha256(new Buffer(hex,'hex')).toString('hex');
};
Network.prototype.start = function(opts, openCallback) {
opts = opts || {}; opts = opts || {};
// Start PeerJS Peer
var self = this; var self = this;
if (this.started) return openCallback(); if (this.started) return openCallback();
opts.connectedPeers = opts.connectedPeers || []; opts.connectedPeers = opts.connectedPeers || [];
this.peerId = this.peerId || opts.peerId;
if (!this.copayerId)
this.setCopayerId(opts.copayerId);
if (!this.signingKey)
this.setSigningKey(opts.signingKeyHex);
this.peer = new Peer(this.peerId, this.opts); this.peer = new Peer(this.peerId, this.opts);
this._setupPeerHandlers(openCallback); this._setupPeerHandlers(openCallback);
@ -238,57 +323,77 @@ Network.prototype.start = function(openCallback, opts) {
this.connectTo(otherPeerId); this.connectTo(otherPeerId);
} }
this.started = true; this.started = true;
console.log('[WebRTC.js.237] started TRUE'); //TODO
}; };
Network.prototype._sendToOne = function(peerId, data, cb) {
Network.prototype._sign = function(payload, copayerId) {
var ret='';
var str = JSON.stringify(payload);
if (payload.type ==='hello') {
ret = (
util.sha512hmac(
new Buffer(str),
new Buffer(copayerId,'hex')
)).toString('hex');
}
else {
if (!this.signingKey)
throw new Error ('no key to sign messages :(');
ret = bitcore.Message.sign(
str,
this.signingKey
).toString('hex');
}
return ret;
};
Network.prototype._sendToOne = function(copayerId, payload, cb) {
var peerId = this.peerFromCopayer(copayerId);
if (peerId !== this.peerId) { if (peerId !== this.peerId) {
var dataConn = this.connections[peerId]; var dataConn = this.connections[peerId];
if (dataConn) { if (dataConn) {
var str = JSON.stringify({ var str = JSON.stringify({
sender: this.peerId, sig: this._sign(payload, copayerId),
data: data payload: payload
}); });
dataConn.send(str); dataConn.send(str);
} }
else { else {
console.log('[WebRTC.js.255] WARN: NO CONNECTION TO:', peerId); //TODO console.log('[WebRTC.js.255] WARN: NO CONNECTION TO:', peerId); //TODO
} }
} }
if (typeof cb === 'function') cb(); if (typeof cb === 'function') cb();
}; };
Network.prototype.send = function(peerIds, data, cb) { Network.prototype.send = function(copayerIds, payload, cb) {
var self=this; var self=this;
if (!peerIds) { if (!copayerIds) {
peerIds = this.connectedPeers; copayerIds = this.connectedCopayers();
data.isBroadcast = 1; payload.isBroadcast = 1;
} }
if (Array.isArray(peerIds)) { if (Array.isArray(copayerIds)) {
var l = peerIds.length; var l = copayerIds.length;
var i = 0; var i = 0;
peerIds.forEach(function(peerId) { copayerIds.forEach(function(copayerId) {
console.log('[WebRTC.js.258:peerId:]',peerId); //TODO self._sendToOne(copayerId, payload, function () {
self._sendToOne(peerId, data, function () {
if (++i === l && typeof cb === 'function') cb(); if (++i === l && typeof cb === 'function') cb();
}); });
}); });
} }
else if (typeof peerIds === 'string') else if (typeof copayerIds === 'string')
self._sendToOne(peerIds, data, cb); self._sendToOne(copayerIds, payload, cb);
}; };
Network.prototype.connectTo = function(peerId) { Network.prototype.connectTo = function(copayerId) {
var self = this; var self = this;
var peerId = this.peerFromCopayer(copayerId);
this._addCopayerMap(peerId,copayerId);
console.log('### STARTING CONNECTION TO:' + peerId ); console.log('### STARTING CONNECTION TO:', peerId, copayerId);
var dataConn = this.peer.connect(peerId, { var dataConn = this.peer.connect(peerId, {
serialization: 'none', serialization: 'none',
reliable: true, reliable: true,
metadata: { message: 'hi copayer!' }
}); });
self._setupConnectionHandlers(dataConn, false); self._setupConnectionHandlers(dataConn, false);

View File

@ -70,7 +70,7 @@ describe('PrivateKey model', function() {
it('should calculate .id', function () { it('should calculate .id', function () {
var w1 = new PrivateKey(config); var w1 = new PrivateKey(config);
should.exist(w1.getId()); should.exist(w1.getId());
w1.getId().length.should.equal(32); w1.getId().length.should.equal(66);
}); });
it('fromObj toObj roundtrip', function () { it('fromObj toObj roundtrip', function () {
var w1 = new PrivateKey(config); var w1 = new PrivateKey(config);

View File

@ -228,7 +228,7 @@ describe('PublicKeyRing model', function() {
networkName: 'livenet', networkName: 'livenet',
}); });
wx.addCopayer(); wx.addCopayer();
(function() { w.merge(wx, true);}).should.throw(); (function() { w.merge(wx);}).should.throw();
}); });