mirror of https://github.com/BTCPrivate/copay.git
Merge pull request #30 from matiu/feature/txproposal
Feature/txproposal
This commit is contained in:
commit
af883c30ec
2
copay.js
2
copay.js
|
@ -2,5 +2,7 @@
|
||||||
module.exports.Storage = require('./js/models/Storage');
|
module.exports.Storage = require('./js/models/Storage');
|
||||||
module.exports.PublicKeyRing = require('./js/models/PublicKeyRing');
|
module.exports.PublicKeyRing = require('./js/models/PublicKeyRing');
|
||||||
module.exports.Wallet = require('./js/models/Wallet');
|
module.exports.Wallet = require('./js/models/Wallet');
|
||||||
|
module.exports.TxProposals = require('./js/models/TxProposals');
|
||||||
module.exports.CopayPeer = require('./js/models/CopayPeer');
|
module.exports.CopayPeer = require('./js/models/CopayPeer');
|
||||||
|
module.exports.PrivateKey = require('./js/models/PrivateKey');
|
||||||
module.exports.FakeStorage = require('./test/FakeStorage');
|
module.exports.FakeStorage = require('./test/FakeStorage');
|
||||||
|
|
14
index.html
14
index.html
|
@ -23,6 +23,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav class="top-bar" data-topbar ng-show="$root.peerId">
|
<nav class="top-bar" data-topbar ng-show="$root.peerId">
|
||||||
|
<!-- TODO : porque el repeat en UL y no en LI?? -->
|
||||||
<ul class="title-area" data-ng-repeat="item in menu" ui-route="/{{item.link}}" data-ng-class="{active: isActive(item)}">
|
<ul class="title-area" data-ng-repeat="item in menu" ui-route="/{{item.link}}" data-ng-class="{active: isActive(item)}">
|
||||||
<li class="name"></li>
|
<li class="name"></li>
|
||||||
<li class="toggle-topbar menu-icon">
|
<li class="toggle-topbar menu-icon">
|
||||||
|
@ -32,8 +33,8 @@
|
||||||
|
|
||||||
<section class="top-bar-section">
|
<section class="top-bar-section">
|
||||||
<!-- Right Nav Section -->
|
<!-- Right Nav Section -->
|
||||||
<ul data-ng-repeat="item in menu" ui-route="/{{item.link}}">
|
<ul>
|
||||||
<li class="large-3 text-center" data-ng-class="{active: isActive(item)}">
|
<li data-ng-repeat="item in menu" ui-route="/{{item.link}}" class="large-2 text-center" data-ng-class="{active: isActive(item)}">
|
||||||
<a href="{{item.link}}"> <i class="fi-home size-16"></i> {{item.title}}</a>
|
<a href="{{item.link}}"> <i class="fi-home size-16"></i> {{item.title}}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -73,11 +74,11 @@
|
||||||
<div class="large-6 columns">
|
<div class="large-6 columns">
|
||||||
<h3>Open a Existing Wallet</h3>
|
<h3>Open a Existing Wallet</h3>
|
||||||
<select class="form-control" >
|
<select class="form-control" >
|
||||||
<option data-ng-repeat="walletId in listWalletIds()" value="walletId">{{walletId}}</option>
|
<option ng-repeat="walletId in listWalletIds()" ng-model="sel" value="walletId">{{walletId}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="large-3 columns">
|
<div class="large-3 columns">
|
||||||
<button class="button primary expand round" type="button" ng-click="open(walletId)">Open</button>
|
<button class="button primary expand round" type="button" ng-click="open(sel || $scope.sel)">Open</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -110,14 +111,14 @@
|
||||||
|
|
||||||
<ul class="no-bullet">
|
<ul class="no-bullet">
|
||||||
<li> [DEBUG] Pubkeys that you have: {{$root.publicKeyRing.registeredCopayers()}}
|
<li> [DEBUG] Pubkeys that you have: {{$root.publicKeyRing.registeredCopayers()}}
|
||||||
|
<li> [DEBUG] WalletId: {{$root.walletId}}
|
||||||
<li class="panel" style="word-wrap: break-word;" ng-repeat="pub in $root.publicKeyRing.copayersBIP32">
|
<li class="panel" style="word-wrap: break-word;" ng-repeat="pub in $root.publicKeyRing.copayersBIP32">
|
||||||
${{pub.extendedPublicKeyString()}}
|
${{pub.extendedPublicKeyString()}}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3 class="panel-title">Copayers ({{$root.connectedPeers.length}}/5)</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="large-6 columns">
|
<div class="large-6 columns">
|
||||||
<h3 class="panel-title">Copayers ({{$root.connectedPeers.length}}/5)</h3>
|
<h3 class="panel-title">Copayers ({{$root.connectedPeers.length}}/{{$root.publicKeyRing.requiredCopayers}})</h3>
|
||||||
<ul class="no-bullet">
|
<ul class="no-bullet">
|
||||||
<li class="panel" ng-repeat="copayer in $root.connectedPeers">
|
<li class="panel" ng-repeat="copayer in $root.connectedPeers">
|
||||||
<span ng-if="copayer == $root.peerId"> You ({{$root.peerId}})<i class="fi-check size-24"></i></span>
|
<span ng-if="copayer == $root.peerId"> You ({{$root.peerId}})<i class="fi-check size-24"></i></span>
|
||||||
|
@ -222,6 +223,7 @@
|
||||||
<script type="text/ng-template" id="send.html">
|
<script type="text/ng-template" id="send.html">
|
||||||
<div class="send" data-ng-controller="SendController">
|
<div class="send" data-ng-controller="SendController">
|
||||||
<h2>{{title}}</h2>
|
<h2>{{title}}</h2>
|
||||||
|
<button class="button primary expand round" type="button" ng-click="sendTest()">sendTest</button>
|
||||||
<form>
|
<form>
|
||||||
<label for="address">To
|
<label for="address">To
|
||||||
<input type="text" id="address" placeholder="Send to">
|
<input type="text" id="address" placeholder="Send to">
|
||||||
|
|
|
@ -5,4 +5,6 @@ var config = {
|
||||||
p2pApiKey: 'lwjd5qra8257b9',
|
p2pApiKey: 'lwjd5qra8257b9',
|
||||||
p2pDebug: 3,
|
p2pDebug: 3,
|
||||||
maxPeers: 5,
|
maxPeers: 5,
|
||||||
|
requiredCopayers: 2,
|
||||||
|
totalCopayers: 3
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,9 @@ angular.module('copay.header').controller('HeaderController',
|
||||||
$scope.menu = [{
|
$scope.menu = [{
|
||||||
'title': 'Home',
|
'title': 'Home',
|
||||||
'link': '#/home'
|
'link': '#/home'
|
||||||
|
}, {
|
||||||
|
'title': 'Copayers',
|
||||||
|
'link': '#/peer'
|
||||||
}, {
|
}, {
|
||||||
'title': 'Transactions',
|
'title': 'Transactions',
|
||||||
'link': '#/transactions'
|
'link': '#/transactions'
|
||||||
|
@ -32,8 +35,12 @@ angular.module('copay.header').controller('HeaderController',
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.signout = function() {
|
$scope.signout = function() {
|
||||||
|
console.log('[header.js.37:signout:]'); //TODO
|
||||||
|
|
||||||
Network.disconnect(function() {
|
Network.disconnect(function() {
|
||||||
|
console.log('[header.js.41] disconnect CB'); //TODO
|
||||||
$location.path('signin');
|
$location.path('signin');
|
||||||
|
$rootScope.$digest();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,43 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copay.send').controller('SendController',
|
angular.module('copay.send').controller('SendController',
|
||||||
function($scope, $rootScope, $location) {
|
function($scope, $rootScope, $location, Network, Storage) {
|
||||||
$scope.title = 'Send';
|
$scope.title = 'Send';
|
||||||
|
|
||||||
if (!$rootScope.peerId) {
|
if (!$rootScope.peerId) {
|
||||||
$location.path('signin');
|
$location.path('signin');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$scope.sendTest = function() {
|
||||||
|
var pkr = $rootScope.publicKeyRing;
|
||||||
|
var txp = $rootScope.txProposals;
|
||||||
|
var opts = {remainderOut: { address: pkr.generateAddress(true).toString() }};
|
||||||
|
|
||||||
|
// From @cmgustavo's wallet
|
||||||
|
var unspentTest = [{
|
||||||
|
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
|
||||||
|
"vout": 1,
|
||||||
|
"amount": 10,
|
||||||
|
"confirmations":7
|
||||||
|
}];
|
||||||
|
|
||||||
|
unspentTest[0].address = pkr.generateAddress(false).toString();
|
||||||
|
unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(false);
|
||||||
|
|
||||||
|
console.log('[send.js.29:txp:] BEFORE',txp); //TODO
|
||||||
|
|
||||||
|
txp.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest,
|
||||||
|
$rootScope.privateKey,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
console.log('[send.js.29:txp:] READY:',txp); //TODO
|
||||||
|
|
||||||
|
Network.storeOpenWallet();
|
||||||
|
Network.sendTxProposals();
|
||||||
|
$rootScope.$digest;
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,6 +25,7 @@ angular.module('copay.signin').controller('SigninController',
|
||||||
$scope.open = function(walletId) {
|
$scope.open = function(walletId) {
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
|
|
||||||
|
console.log('[signin.js.28:walletId:]',walletId); //TODO
|
||||||
if (Network.openWallet(walletId)) {
|
if (Network.openWallet(walletId)) {
|
||||||
Network.init(function() {
|
Network.init(function() {
|
||||||
$location.path('peer');
|
$location.path('peer');
|
||||||
|
@ -43,8 +44,6 @@ angular.module('copay.signin').controller('SigninController',
|
||||||
$location.path('peer');
|
$location.path('peer');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
}, function() {
|
}, function() {
|
||||||
|
|
||||||
console.log('[signin.js.46] SETTING MESSAGE'); //TODO
|
|
||||||
$rootScope.flashMessage = { message: 'Connection refussed', type: 'error'};
|
$rootScope.flashMessage = { message: 'Connection refussed', type: 'error'};
|
||||||
$location.path('home');
|
$location.path('home');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
|
|
2958
js/copayBundle.js
2958
js/copayBundle.js
File diff suppressed because it is too large
Load Diff
|
@ -67,6 +67,7 @@ CopayPeer.prototype._showConnectedPeers = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype._onClose = function(peerId) {
|
CopayPeer.prototype._onClose = function(peerId) {
|
||||||
|
console.log('[CopayPeer.js.70] _onClose'); //TODO
|
||||||
this.connectedPeers = CopayPeer._arrayRemove(peerId, this.connectedPeers);
|
this.connectedPeers = CopayPeer._arrayRemove(peerId, this.connectedPeers);
|
||||||
this._notify();
|
this._notify();
|
||||||
};
|
};
|
||||||
|
@ -92,7 +93,7 @@ CopayPeer.prototype._onData = function(data, isInbound) {
|
||||||
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);
|
console.log('### RECEIVED TYPE: %s FROM %s', obj.data.type, obj.sender, obj.data);
|
||||||
|
|
||||||
switch(obj.data.type) {
|
switch(obj.data.type) {
|
||||||
case 'peerList':
|
case 'peerList':
|
||||||
|
@ -156,13 +157,16 @@ CopayPeer.prototype._setupConnectionHandlers = function(
|
||||||
});
|
});
|
||||||
|
|
||||||
dataConn.on('close', function() {
|
dataConn.on('close', function() {
|
||||||
console.log('### CLOSE RECV FROM:', dataConn.peer); //TODO
|
if (self.closing) return;
|
||||||
|
console.log('### CLOSE RECV FROM:', dataConn.peer);
|
||||||
|
|
||||||
self._onClose(dataConn.peer);
|
self._onClose(dataConn.peer);
|
||||||
if (typeof closeCallback === 'function') closeCallback();
|
if (typeof closeCallback === 'function') closeCallback();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype._notify = function(newPeer) {
|
CopayPeer.prototype._notify = function(newPeer) {
|
||||||
|
console.log('[CopayPeer.js.168:_notify:]'); //TODO
|
||||||
this._showConnectedPeers();
|
this._showConnectedPeers();
|
||||||
this.emit('networkChange', newPeer);
|
this.emit('networkChange', newPeer);
|
||||||
};
|
};
|
||||||
|
@ -182,6 +186,10 @@ CopayPeer.prototype._setupPeerHandlers = function(openCallback) {
|
||||||
|
|
||||||
p.on('error', function(err) {
|
p.on('error', function(err) {
|
||||||
console.log('### PEER ERROR:', err);
|
console.log('### PEER ERROR:', err);
|
||||||
|
self.peer.disconnect();
|
||||||
|
self.peer.destroy();
|
||||||
|
self.peer = null;
|
||||||
|
this.emit('abort');
|
||||||
});
|
});
|
||||||
|
|
||||||
p.on('connection', function(dataConn) {
|
p.on('connection', function(dataConn) {
|
||||||
|
@ -236,7 +244,6 @@ CopayPeer.prototype.send = function(peerIds, data, cb) {
|
||||||
peerIds = this.connectedPeers;
|
peerIds = this.connectedPeers;
|
||||||
data.isBroadcast = 1;
|
data.isBroadcast = 1;
|
||||||
}
|
}
|
||||||
console.log('[CopayPeer.js.216:SENDD:]',data); //TODO
|
|
||||||
|
|
||||||
if (Array.isArray(peerIds)) {
|
if (Array.isArray(peerIds)) {
|
||||||
var l = peerIds.length;
|
var l = peerIds.length;
|
||||||
|
@ -267,9 +274,13 @@ CopayPeer.prototype.connectTo = function(peerId, openCallback, closeCallback ) {
|
||||||
};
|
};
|
||||||
|
|
||||||
CopayPeer.prototype.disconnect = function(peerId, cb) {
|
CopayPeer.prototype.disconnect = function(peerId, cb) {
|
||||||
|
console.log('[CopayPeer.js.268:disconnect:]'); //TODO
|
||||||
var self = this;
|
var self = this;
|
||||||
|
self.closing = 1;
|
||||||
|
|
||||||
this.send(null, { type: 'disconnect' }, function() {
|
this.send(null, { type: 'disconnect' }, function() {
|
||||||
|
|
||||||
|
console.log('[CopayPeer.js.273] disconnect CB'); //TODO
|
||||||
self.connectedPeers = [];
|
self.connectedPeers = [];
|
||||||
self.peerId = null;
|
self.peerId = null;
|
||||||
if (self.peer) {
|
if (self.peer) {
|
||||||
|
@ -277,6 +288,7 @@ CopayPeer.prototype.disconnect = function(peerId, cb) {
|
||||||
self.peer.destroy();
|
self.peer.destroy();
|
||||||
self.peer = null;
|
self.peer = null;
|
||||||
}
|
}
|
||||||
|
self.closing = 0;
|
||||||
if (typeof cb === 'function') cb();
|
if (typeof cb === 'function') cb();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
var imports = require('soop').imports();
|
||||||
|
var bitcore = require('bitcore');
|
||||||
|
var BIP32 = bitcore.BIP32;
|
||||||
|
var WalletKey = bitcore.WalletKey;
|
||||||
|
var networks = bitcore.networks;
|
||||||
|
var util = bitcore.util;
|
||||||
|
var PublicKeyRing = require('./PublicKeyRing');
|
||||||
|
|
||||||
|
function PrivateKey(opts) {
|
||||||
|
this.network = opts.networkName === 'testnet' ?
|
||||||
|
networks.testnet : networks.livenet;
|
||||||
|
var init = opts.extendedPrivateKeyString || this.network.name;
|
||||||
|
this.BIP32 = opts.BIP32 || new BIP32(init);
|
||||||
|
this._calcId();
|
||||||
|
};
|
||||||
|
|
||||||
|
PrivateKey.prototype._calcId = function() {
|
||||||
|
this.id = util.ripe160(this.BIP32.extendedPublicKey).toString('hex');
|
||||||
|
};
|
||||||
|
|
||||||
|
PrivateKey.prototype.getBIP32 = function(index,isChange) {
|
||||||
|
if (typeof index === 'undefined') {
|
||||||
|
return this.BIP32;
|
||||||
|
}
|
||||||
|
return this.BIP32.derive( isChange ?
|
||||||
|
PublicKeyRing.ChangeBranch(index):PublicKeyRing.PublicBranch(index) );
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
PrivateKey.fromObj = function(o) {
|
||||||
|
return new PrivateKey({
|
||||||
|
extendedPrivateKeyString: o.extendedPrivateKeyString,
|
||||||
|
networkName: o.networkName,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
PrivateKey.prototype.toObj = function() {
|
||||||
|
return {
|
||||||
|
extendedPrivateKeyString: this.BIP32.extendedPrivateKeyString(),
|
||||||
|
networkName: this.network.name,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
PrivateKey.prototype.get = function(index,isChange) {
|
||||||
|
var derivedBIP32 = this.getBIP32(index,isChange);
|
||||||
|
var wk = new WalletKey({network: this.network});
|
||||||
|
var p = derivedBIP32.eckey.private.toString('hex');
|
||||||
|
wk.fromObj({priv: p});
|
||||||
|
return wk;
|
||||||
|
};
|
||||||
|
|
||||||
|
PrivateKey.prototype.getAll = function(addressIndex, changeAddressIndex) {
|
||||||
|
var ret = [];
|
||||||
|
|
||||||
|
for(var i=0;i<addressIndex; i++) {
|
||||||
|
ret.push(this.get(i,false));
|
||||||
|
}
|
||||||
|
for(var i=0; i<changeAddressIndex; i++) {
|
||||||
|
ret.push(this.get(i,true));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = require('soop')(PrivateKey);
|
|
@ -14,17 +14,6 @@ var buffertools = bitcore.buffertools;
|
||||||
var Storage = imports.Storage || require('./Storage');
|
var Storage = imports.Storage || require('./Storage');
|
||||||
var storage = Storage.default();
|
var storage = Storage.default();
|
||||||
|
|
||||||
/*
|
|
||||||
* This follow Electrum convetion, as described in
|
|
||||||
* https://bitcointalk.org/index.php?topic=274182.0
|
|
||||||
*
|
|
||||||
* We should probably adopt the next standard once it's ready, as discussed in:
|
|
||||||
* http://sourceforge.net/p/bitcoin/mailman/message/32148600/
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
var PUBLIC_BRANCH = 'm/0/';
|
|
||||||
var CHANGE_BRANCH = 'm/1/';
|
|
||||||
|
|
||||||
function PublicKeyRing(opts) {
|
function PublicKeyRing(opts) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
|
@ -37,16 +26,32 @@ function PublicKeyRing(opts) {
|
||||||
|
|
||||||
this.id = opts.id || PublicKeyRing.getRandomId();
|
this.id = opts.id || PublicKeyRing.getRandomId();
|
||||||
|
|
||||||
this.dirty = 1;
|
|
||||||
this.copayersBIP32 = [];
|
this.copayersBIP32 = [];
|
||||||
|
|
||||||
this.changeAddressIndex=0;
|
this.changeAddressIndex=0;
|
||||||
this.addressIndex=0;
|
this.addressIndex=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This follow Electrum convetion, as described in
|
||||||
|
* https://bitcointalk.org/index.php?topic=274182.0
|
||||||
|
*
|
||||||
|
* We should probably adopt the next standard once it's ready, as discussed in:
|
||||||
|
* http://sourceforge.net/p/bitcoin/mailman/message/32148600/
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
PublicKeyRing.PublicBranch = function (index) {
|
||||||
|
return 'm/0/'+index;
|
||||||
|
};
|
||||||
|
|
||||||
|
PublicKeyRing.ChangeBranch = function (index) {
|
||||||
|
return 'm/1/'+index;
|
||||||
|
};
|
||||||
|
|
||||||
PublicKeyRing.getRandomId = function () {
|
PublicKeyRing.getRandomId = function () {
|
||||||
return buffertools.toHex(coinUtil.generateNonce());
|
var r = buffertools.toHex(coinUtil.generateNonce());
|
||||||
|
return r;
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.decrypt = function (passphrase, encPayload) {
|
PublicKeyRing.decrypt = function (passphrase, encPayload) {
|
||||||
|
@ -60,41 +65,39 @@ PublicKeyRing.encrypt = function (passphrase, payload) {
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.fromObj = function (data) {
|
PublicKeyRing.fromObj = function (data) {
|
||||||
|
if (!data.ts) {
|
||||||
|
throw new Error('bad data format: Did you use .toObj()?');
|
||||||
|
}
|
||||||
var config = { networkName: data.networkName || 'livenet' };
|
var config = { networkName: data.networkName || 'livenet' };
|
||||||
|
|
||||||
var w = new PublicKeyRing(config);
|
var w = new PublicKeyRing(config);
|
||||||
|
|
||||||
|
w.id = data.id;
|
||||||
w.requiredCopayers = data.requiredCopayers;
|
w.requiredCopayers = data.requiredCopayers;
|
||||||
w.totalCopayers = data.totalCopayers;
|
w.totalCopayers = data.totalCopayers;
|
||||||
w.addressIndex = data.addressIndex;
|
w.addressIndex = data.addressIndex;
|
||||||
w.changeAddressIndex = data.changeAddressIndex;
|
w.changeAddressIndex = data.changeAddressIndex;
|
||||||
|
|
||||||
// this.bip32 = ;
|
|
||||||
w.copayersBIP32 = data.copayersExtPubKeys.map( function (pk) {
|
w.copayersBIP32 = data.copayersExtPubKeys.map( function (pk) {
|
||||||
return new BIP32(pk);
|
return new BIP32(pk);
|
||||||
});
|
});
|
||||||
|
|
||||||
w.dirty = 0;
|
w.ts = data.ts;
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.read = function (id, passphrase) {
|
PublicKeyRing.read = function (encPayload, id, passphrase) {
|
||||||
var encPayload = storage.get(id);
|
|
||||||
if (!encPayload)
|
if (!encPayload)
|
||||||
throw new Error('Could not find wallet data');
|
throw new Error('Could not find wallet data');
|
||||||
var data;
|
var data;
|
||||||
try {
|
try {
|
||||||
data = JSON.parse( PublicKeyRing.decrypt( passphrase, encPayload ));
|
data = JSON.parse( PublicKeyRing.decrypt( passphrase, encPayload ));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error('error in storage: '+ e.toString());
|
throw new Error('error in read: '+ e.toString());
|
||||||
return;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if (data.id !== id)
|
if (data.id !== id)
|
||||||
throw new Error('Wrong id in data');
|
throw new Error('Wrong id in data');
|
||||||
|
|
||||||
|
|
||||||
return PublicKeyRing.fromObj(data);
|
return PublicKeyRing.fromObj(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -119,15 +122,11 @@ PublicKeyRing.prototype.serialize = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
PublicKeyRing.prototype.store = function (passphrase) {
|
PublicKeyRing.prototype.toStore = function (passphrase) {
|
||||||
|
|
||||||
if (!this.id)
|
if (!this.id)
|
||||||
throw new Error('wallet has no id');
|
throw new Error('wallet has no id');
|
||||||
|
|
||||||
storage.set(this.id, PublicKeyRing.encrypt(passphrase,this.serialize()));
|
return PublicKeyRing.encrypt(passphrase,this.serialize());
|
||||||
this.dirty = 0;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype.registeredCopayers = function () {
|
PublicKeyRing.prototype.registeredCopayers = function () {
|
||||||
|
@ -136,13 +135,13 @@ PublicKeyRing.prototype.registeredCopayers = function () {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PublicKeyRing.prototype.haveAllRequiredPubKeys = function () {
|
PublicKeyRing.prototype.isComplete = function () {
|
||||||
return this.registeredCopayers() >= this.totalCopayers;
|
return this.registeredCopayers() >= this.totalCopayers;
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype._checkKeys = function() {
|
PublicKeyRing.prototype._checkKeys = function() {
|
||||||
|
|
||||||
if (!this.haveAllRequiredPubKeys())
|
if (!this.isComplete())
|
||||||
throw new Error('dont have required keys yet');
|
throw new Error('dont have required keys yet');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -154,7 +153,7 @@ PublicKeyRing.prototype._newExtendedPublicKey = function () {
|
||||||
|
|
||||||
PublicKeyRing.prototype.addCopayer = function (newEpk) {
|
PublicKeyRing.prototype.addCopayer = function (newEpk) {
|
||||||
|
|
||||||
if (this.haveAllRequiredPubKeys())
|
if (this.isComplete())
|
||||||
throw new Error('already have all required key:' + this.totalCopayers);
|
throw new Error('already have all required key:' + this.totalCopayers);
|
||||||
|
|
||||||
if (!newEpk) {
|
if (!newEpk) {
|
||||||
|
@ -167,18 +166,17 @@ PublicKeyRing.prototype.addCopayer = function (newEpk) {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.copayersBIP32.push(new BIP32(newEpk));
|
this.copayersBIP32.push(new BIP32(newEpk));
|
||||||
this.dirty = 1;
|
|
||||||
return newEpk;
|
return newEpk;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
PublicKeyRing.prototype.getCopayersPubKeys = function (index, isChange) {
|
PublicKeyRing.prototype.getPubKeys = function (index, isChange) {
|
||||||
this._checkKeys();
|
this._checkKeys();
|
||||||
|
|
||||||
var pubKeys = [];
|
var pubKeys = [];
|
||||||
var l = this.copayersBIP32.length;
|
var l = this.copayersBIP32.length;
|
||||||
for(var i=0; i<l; i++) {
|
for(var i=0; i<l; i++) {
|
||||||
var path = (isChange ? CHANGE_BRANCH : PUBLIC_BRANCH) + index;
|
var path = isChange ? PublicKeyRing.ChangeBranch(index) : PublicKeyRing.PublicBranch(index);
|
||||||
var bip32 = this.copayersBIP32[i].derive(path);
|
var bip32 = this.copayersBIP32[i].derive(path);
|
||||||
pubKeys[i] = bip32.eckey.public;
|
pubKeys[i] = bip32.eckey.public;
|
||||||
}
|
}
|
||||||
|
@ -197,21 +195,30 @@ PublicKeyRing.prototype._checkIndexRange = function (index, isChange) {
|
||||||
PublicKeyRing.prototype.getRedeemScript = function (index, isChange) {
|
PublicKeyRing.prototype.getRedeemScript = function (index, isChange) {
|
||||||
this._checkIndexRange(index, isChange);
|
this._checkIndexRange(index, isChange);
|
||||||
|
|
||||||
var pubKeys = this.getCopayersPubKeys(index, isChange);
|
var pubKeys = this.getPubKeys(index, isChange);
|
||||||
var script = Script.createMultisig(this.requiredCopayers, pubKeys);
|
var script = Script.createMultisig(this.requiredCopayers, pubKeys);
|
||||||
return script;
|
return script;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
PublicKeyRing.prototype.getAddress = function (index, isChange) {
|
PublicKeyRing.prototype.getAddress = function (index, isChange) {
|
||||||
this._checkIndexRange(index, isChange);
|
this._checkIndexRange(index, isChange);
|
||||||
|
|
||||||
var script = this.getRedeemScript(index,isChange);
|
var script = this.getRedeemScript(index,isChange);
|
||||||
var hash = coinUtil.sha256ripe160(script.getBuffer());
|
var hash = coinUtil.sha256ripe160(script.getBuffer());
|
||||||
var version = this.network.addressScript;
|
var version = this.network.P2SHVersion;
|
||||||
var addr = new Address(version, hash);
|
var addr = new Address(version, hash);
|
||||||
return addr.as('base58');
|
return addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PublicKeyRing.prototype.getScriptPubKeyHex = function (index, isChange) {
|
||||||
|
this._checkIndexRange(index, isChange);
|
||||||
|
var addr = this.getAddress(index,isChange);
|
||||||
|
return Script.createP2SH(addr.payload()).getBuffer().toString('hex');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//generate a new address, update index.
|
//generate a new address, update index.
|
||||||
PublicKeyRing.prototype.generateAddress = function(isChange) {
|
PublicKeyRing.prototype.generateAddress = function(isChange) {
|
||||||
|
|
||||||
|
@ -239,18 +246,28 @@ PublicKeyRing.prototype.getAddresses = function() {
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype._checkInPRK = function(inPKR, ignoreId) {
|
PublicKeyRing.prototype.getRedeemScriptMap = function () {
|
||||||
|
var ret = {};
|
||||||
|
|
||||||
|
for (var i=0; i<this.changeAddressIndex; i++) {
|
||||||
if (!inPKR.ts) {
|
ret[this.getAddress(i,true)] = this.getRedeemScript(i,true).getBuffer().toString('hex');
|
||||||
throw new Error('inPRK bad format: Did you use .toObj()?');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (var i=0; i<this.addressIndex; i++) {
|
||||||
|
ret[this.getAddress(i)] = this.getRedeemScript(i).getBuffer().toString('hex');
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
PublicKeyRing.prototype._checkInPRK = function(inPKR, ignoreId) {
|
||||||
|
|
||||||
if (!ignoreId && this.id !== inPKR.id) {
|
if (!ignoreId && this.id !== inPKR.id) {
|
||||||
throw new Error('inPRK id mismatch');
|
throw new Error('inPRK id mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.network.name !== inPKR.networkName)
|
if (this.network.name !== inPKR.network.name)
|
||||||
throw new Error('inPRK network mismatch');
|
throw new Error('inPRK network mismatch');
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -262,9 +279,6 @@ PublicKeyRing.prototype._checkInPRK = function(inPKR, ignoreId) {
|
||||||
this.totalCopayers && inPKR.totalCopayers &&
|
this.totalCopayers && inPKR.totalCopayers &&
|
||||||
(this.totalCopayers !== inPKR.totalCopayers))
|
(this.totalCopayers !== inPKR.totalCopayers))
|
||||||
throw new Error('inPRK requiredCopayers mismatch');
|
throw new Error('inPRK requiredCopayers mismatch');
|
||||||
|
|
||||||
if (! inPKR.ts)
|
|
||||||
throw new Error('no ts at inPRK');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -285,13 +299,13 @@ PublicKeyRing.prototype._mergeIndexes = function(inPKR) {
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype._mergePubkeys = function(inPKR) {
|
PublicKeyRing.prototype._mergePubkeys = function(inPKR) {
|
||||||
var hasChanged = false;
|
|
||||||
var l= this.copayersBIP32.length;
|
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var hasChanged = false;
|
||||||
|
var l= self.copayersBIP32.length;
|
||||||
|
|
||||||
inPKR.copayersExtPubKeys.forEach( function(epk) {
|
inPKR.copayersBIP32.forEach( function(b) {
|
||||||
var haveIt = false;
|
var haveIt = false;
|
||||||
|
var epk = b.extendedPublicKeyString();
|
||||||
for(var j=0; j<l; j++) {
|
for(var j=0; j<l; j++) {
|
||||||
if (self.copayersBIP32[j].extendedPublicKeyString() === epk) {
|
if (self.copayersBIP32[j].extendedPublicKeyString() === epk) {
|
||||||
haveIt=true;
|
haveIt=true;
|
||||||
|
@ -299,6 +313,10 @@ PublicKeyRing.prototype._mergePubkeys = function(inPKR) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!haveIt) {
|
if (!haveIt) {
|
||||||
|
if (self.isComplete()) {
|
||||||
|
console.log('[PublicKeyRing.js.318] REPEATED KEY', epk); //TODO
|
||||||
|
throw new Error('trying to add more pubkeys, when PKR isComplete at merge');
|
||||||
|
}
|
||||||
self.copayersBIP32.push(new BIP32(epk));
|
self.copayersBIP32.push(new BIP32(epk));
|
||||||
hasChanged=true;
|
hasChanged=true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,200 @@
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
var imports = require('soop').imports();
|
||||||
|
var bitcore = require('bitcore');
|
||||||
|
var util = bitcore.util;
|
||||||
|
var Transaction = bitcore.Transaction;
|
||||||
|
var Builder = bitcore.TransactionBuilder;
|
||||||
|
var Script = bitcore.Script;
|
||||||
|
var buffertools = bitcore.buffertools;
|
||||||
|
|
||||||
|
var Storage = imports.Storage || require('./Storage');
|
||||||
|
var storage = Storage.default();
|
||||||
|
|
||||||
|
function TxProposal(opts) {
|
||||||
|
this.seenBy = opts.seenBy || {};
|
||||||
|
this.signedBy = opts.signedBy || {};
|
||||||
|
this.builder = opts.builder;
|
||||||
|
};
|
||||||
|
module.exports = require('soop')(TxProposal);
|
||||||
|
|
||||||
|
|
||||||
|
function TxProposals(opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
this.walletId = opts.walletId;
|
||||||
|
this.network = opts.networkName === 'livenet' ?
|
||||||
|
bitcore.networks.livenet : bitcore.networks.testnet;
|
||||||
|
this.publicKeyRing = opts.publicKeyRing;
|
||||||
|
this.txps = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
TxProposals.fromObj = function(o) {
|
||||||
|
var ret = new TxProposals({
|
||||||
|
networkName: o.networkName,
|
||||||
|
walletId: o.walletId,
|
||||||
|
});
|
||||||
|
o.txps.forEach(function(t) {
|
||||||
|
ret.txps.push({
|
||||||
|
seenBy: t.seenBy,
|
||||||
|
signedBy: t.signedBy,
|
||||||
|
builder: new Builder.fromObj(t.builderObj),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TxProposals.prototype.toObj = function() {
|
||||||
|
var ret = [];
|
||||||
|
this.txps.forEach(function(t) {
|
||||||
|
ret.push({
|
||||||
|
seenBy: t.seenBy,
|
||||||
|
signedBy: t.signedBy,
|
||||||
|
builderObj: t.builder.toObj(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
txps: ret,
|
||||||
|
walletId: this.walletId,
|
||||||
|
networkName: this.network.name,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TxProposals.prototype._getNormHash = function(txps) {
|
||||||
|
var ret = {};
|
||||||
|
txps.forEach(function(txp) {
|
||||||
|
var hash = txp.builder.build().getNormalizedHash().toString('hex');
|
||||||
|
ret[hash]=txp;
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
TxProposals.prototype._startMerge = function(myTxps, theirTxps) {
|
||||||
|
var fromUs=0, fromTheirs=0, merged =0;
|
||||||
|
var toMerge = {}, ready=[];
|
||||||
|
|
||||||
|
Object.keys(theirTxps).forEach(function(hash) {
|
||||||
|
if (!myTxps[hash]) {
|
||||||
|
ready.push(theirTxps[hash]); // only in theirs;
|
||||||
|
fromTheirs++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toMerge[hash]=theirTxps[hash]; // need Merging
|
||||||
|
merged++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(myTxps).forEach(function(hash) {
|
||||||
|
if(!toMerge[hash]) {
|
||||||
|
ready.push(myTxps[hash]); // only in myTxps;
|
||||||
|
fromUs++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
stats: {
|
||||||
|
fromUs: fromUs,
|
||||||
|
fromTheirs: fromTheirs,
|
||||||
|
merged: merged,
|
||||||
|
},
|
||||||
|
ready: ready,
|
||||||
|
toMerge: toMerge,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
TxProposals.prototype._mergeMetadata = function(myTxps, theirTxps, mergeInfo) {
|
||||||
|
|
||||||
|
var toMerge = mergeInfo.toMerge;
|
||||||
|
|
||||||
|
Object.keys(toMerge).forEach(function(hash) {
|
||||||
|
var v0 = myTxps[hash];
|
||||||
|
var v1 = toMerge[hash];
|
||||||
|
|
||||||
|
Object.keys(v1.seenBy).forEach(function(k) {
|
||||||
|
v0.seenBy[k] = v1.seenBy[k];
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(v1.signedBy).forEach(function(k) {
|
||||||
|
v0.signedBy[k] = v1.signedBy[k];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TxProposals.prototype._mergeBuilder = function(myTxps, theirTxps, mergeInfo) {
|
||||||
|
var self = this;
|
||||||
|
var toMerge = mergeInfo.toMerge;
|
||||||
|
|
||||||
|
Object.keys(toMerge).forEach(function(hash) {
|
||||||
|
var v0 = myTxps[hash].builder;
|
||||||
|
var v1 = toMerge[hash].builder;
|
||||||
|
|
||||||
|
v0.merge(v1);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TxProposals.prototype.merge = function(t) {
|
||||||
|
if (this.network.name !== t.network.name)
|
||||||
|
throw new Error('network mismatch in:', t);
|
||||||
|
|
||||||
|
var res = [];
|
||||||
|
|
||||||
|
var myTxps = this._getNormHash(this.txps);
|
||||||
|
var theirTxps = this._getNormHash(t.txps);
|
||||||
|
|
||||||
|
var mergeInfo = this._startMerge(myTxps, theirTxps);
|
||||||
|
this._mergeMetadata(myTxps, theirTxps, mergeInfo);
|
||||||
|
this._mergeBuilder(myTxps, theirTxps, mergeInfo);
|
||||||
|
|
||||||
|
Object.keys(mergeInfo.toMerge).forEach(function(hash) {
|
||||||
|
mergeInfo.ready.push(myTxps[hash]);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.txps=mergeInfo.ready;
|
||||||
|
return mergeInfo.stats;
|
||||||
|
};
|
||||||
|
|
||||||
|
TxProposals.prototype.create = function(toAddress, amountSatStr, utxos, priv, opts) {
|
||||||
|
var pkr = this.publicKeyRing;
|
||||||
|
opts = opts || {};
|
||||||
|
|
||||||
|
var amountSat = bitcore.bignum(amountSatStr);
|
||||||
|
|
||||||
|
if (! pkr.isComplete() ) {
|
||||||
|
throw new Error('publicKeyRing is not complete');
|
||||||
|
}
|
||||||
|
|
||||||
|
var opts = {
|
||||||
|
remainderOut: opts.remainderOut || { address: pkr.generateAddress(true).toString() }
|
||||||
|
};
|
||||||
|
|
||||||
|
var b = new Builder(opts)
|
||||||
|
.setUnspent(utxos)
|
||||||
|
.setHashToScriptMap(pkr.getRedeemScriptMap())
|
||||||
|
.setOutputs([{address: toAddress, amountSat: amountSat}])
|
||||||
|
;
|
||||||
|
|
||||||
|
var signRet;
|
||||||
|
if (priv) {
|
||||||
|
b.sign( priv.getAll(pkr.addressIndex, pkr.changeAddressIndex) );
|
||||||
|
}
|
||||||
|
var me = {};
|
||||||
|
if (priv) me[priv.id] = Date.now();
|
||||||
|
|
||||||
|
this.txps.push(
|
||||||
|
new TxProposal({
|
||||||
|
signedBy: priv && b.signaturesAdded ? me : {},
|
||||||
|
seenBy: priv ? me : {},
|
||||||
|
builder: b,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
TxProposals.prototype.sign = function(index) {
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = require('soop')(TxProposals);
|
|
@ -20,14 +20,14 @@ angular.module('copay.network')
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// set new inbound connections
|
// set new inbound connections
|
||||||
var _setNewPeer = function(newPeer) {
|
var _setNewPeer = function(newPeer) {
|
||||||
var cp = $rootScope.cp;
|
var cp = $rootScope.cp;
|
||||||
console.log('#### SENDING PKR 1111 ');
|
console.log('#### Setting new PEER:', newPeer);
|
||||||
cp.send(newPeer, {
|
sendPublicKeyRing(newPeer);
|
||||||
type: 'publicKeyRing',
|
sendTxProposals(newPeer);
|
||||||
publicKeyRing: $rootScope.publicKeyRing.toObj(),
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var _handleNetworkChange = function(newPeer) {
|
var _handleNetworkChange = function(newPeer) {
|
||||||
|
@ -40,32 +40,84 @@ angular.module('copay.network')
|
||||||
_refreshUx();
|
_refreshUx();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO -> probably not in network.js
|
||||||
|
var storeOpenWallet = function() {
|
||||||
|
var id = $rootScope.walletId;
|
||||||
|
Storage.addWalletId(id);
|
||||||
|
Storage.set(id, 'publicKeyRing',$rootScope.publicKeyRing.toObj());
|
||||||
|
Storage.set(id, 'privateKey', $rootScope.privateKey.toObj());
|
||||||
|
Storage.set(id, 'txProposals', $rootScope.txProposals.toObj());
|
||||||
|
console.log('\t### Wallet Stored');
|
||||||
|
};
|
||||||
|
|
||||||
// TODO -> probably not in network.js
|
// TODO -> probably not in network.js
|
||||||
var createWallet = function(walletId) {
|
var createWallet = function(walletId) {
|
||||||
|
|
||||||
console.log('### CREATING WALLET. ID:' + walletId);
|
console.log('### CREATING WALLET. ID:' + walletId);
|
||||||
|
|
||||||
|
var priv = new copay.PrivateKey({networkName: config.networkName});
|
||||||
|
console.log('\t### PrivateKey Initialized');
|
||||||
|
//TODO
|
||||||
|
console.log('[PRIVATE]',priv.toObj()); //TODO
|
||||||
|
|
||||||
//TODO create a wallet and WalletId, not only pkr
|
//TODO create a wallet and WalletId, not only pkr
|
||||||
var pkr = new copay.PublicKeyRing({
|
var pkr = new copay.PublicKeyRing({
|
||||||
network: config.networkName,
|
networkName: config.networkName,
|
||||||
id: walletId,
|
id: walletId,
|
||||||
|
requiredCopayers: config.requiredCopayers || 3, // TODO set per wallet
|
||||||
|
totalCopayers: config.totalCopayers || 5,
|
||||||
});
|
});
|
||||||
pkr.addCopayer();
|
|
||||||
console.log('\t### PublicKeyRing Initialized:');
|
|
||||||
Storage.addWalletId(pkr.id);
|
|
||||||
Storage.set(pkr.id, 'publicKeyRing', pkr.toObj());
|
|
||||||
|
|
||||||
$rootScope.walletId = pkr.id;
|
console.log('[network.js.70] WALLET ID IS:', pkr.id); //TODO
|
||||||
$rootScope.publicKeyRing = pkr;
|
|
||||||
|
// Add self to the ring.
|
||||||
|
pkr.addCopayer(priv.getBIP32().extendedPublicKeyString());
|
||||||
|
|
||||||
|
console.log('\t### PublicKeyRing Initialized');
|
||||||
|
|
||||||
|
var txp = new copay.TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
walletId: pkr.id,
|
||||||
|
});
|
||||||
|
console.log('\t### TxProposals Initialized');
|
||||||
|
|
||||||
|
// Store it on rootScope
|
||||||
|
$rootScope.walletId = pkr.id;
|
||||||
|
$rootScope.privateKey = priv; // TODO secure this.
|
||||||
|
$rootScope.publicKeyRing = pkr;
|
||||||
|
$rootScope.txProposals = txp;
|
||||||
|
|
||||||
|
storeOpenWallet();
|
||||||
};
|
};
|
||||||
|
|
||||||
var openWallet = function (walletId) {
|
var openWallet = function (walletId) {
|
||||||
var ret = false;
|
console.log('[network.js.90:openWallet:]',walletId); //TODO
|
||||||
var pkr = Storage.get(walletId, 'publicKeyRing');
|
|
||||||
|
|
||||||
if (pkr) {
|
var ret = false;
|
||||||
|
var pkr = Storage.get(walletId, 'publicKeyRing');
|
||||||
|
var priv = Storage.get(walletId, 'privateKey');
|
||||||
|
var txp = Storage.get(walletId, 'txProposals');
|
||||||
|
|
||||||
|
console.log('[network.js.96:pkr:]',pkr); //TODO
|
||||||
|
console.log('[network.js.97:priv:]',priv); //TODO
|
||||||
|
|
||||||
|
|
||||||
|
if (pkr && pkr.copayersExtPubKeys.length && priv) {
|
||||||
console.log('### WALLET OPENED:', walletId, pkr);
|
console.log('### WALLET OPENED:', walletId, pkr);
|
||||||
$rootScope.walletId = walletId;
|
$rootScope.walletId = walletId;
|
||||||
$rootScope.publicKeyRing = new copay.PublicKeyRing.fromObj(pkr);
|
$rootScope.publicKeyRing = new copay.PublicKeyRing.fromObj(pkr);
|
||||||
|
$rootScope.txProposals = new copay.TxProposals.fromObj(txp);
|
||||||
|
$rootScope.PrivateKey = new copay.PrivateKey.fromObj(priv); //TODO secure
|
||||||
|
|
||||||
|
// JIC: Add our key
|
||||||
|
try {
|
||||||
|
$rootScope.publicKeyRing.addCopayer(
|
||||||
|
$rootScope.PrivateKey.getBIP32().extendedPublicKeyString()
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.log('NOT NECCESARY AN ERROR:', e); //TODO
|
||||||
|
};
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -75,52 +127,111 @@ angular.module('copay.network')
|
||||||
console.log('### CLOSING WALLET');
|
console.log('### CLOSING WALLET');
|
||||||
$rootScope.walletId = null;
|
$rootScope.walletId = null;
|
||||||
$rootScope.publicKeyRing = null;
|
$rootScope.publicKeyRing = null;
|
||||||
//TODO
|
$rootScope.privateKey = null;
|
||||||
|
$rootScope.txProposals = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
var _checkWallet = function(walletId) {
|
var _checkWallet = function(walletId, allowChange) {
|
||||||
console.log('[network.js.79:_checkWallet:]',walletId); //TODO
|
console.log('[network.js.79:_checkWallet:]',walletId); //TODO
|
||||||
|
|
||||||
if ($rootScope.walletId && $rootScope.walletId !== walletId)
|
if ($rootScope.walletId && $rootScope.walletId === walletId)
|
||||||
closeWallet();
|
return;
|
||||||
|
|
||||||
|
if ($rootScope.walletId && $rootScope.walletId !== walletId) {
|
||||||
|
throw new Error('message to wrong walletID');
|
||||||
|
}
|
||||||
|
|
||||||
if ($rootScope.walletId)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!openWallet(walletId)) {
|
if (!openWallet(walletId)) {
|
||||||
createWallet(walletId);
|
createWallet(walletId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var _handleData = function(senderId, data, isInbound) {
|
|
||||||
|
var sendTxProposals = function(recipients) {
|
||||||
var cp = $rootScope.cp;
|
var cp = $rootScope.cp;
|
||||||
|
console.log('### SENDING txProposals TO:', recipients||'All', $rootScope.txProposals);
|
||||||
|
cp.send( recipients, {
|
||||||
|
type: 'txProposals',
|
||||||
|
txProposals: $rootScope.txProposals.toObj(),
|
||||||
|
walletId: $rootScope.walletId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var sendPublicKeyRing = function(recipients) {
|
||||||
|
var cp = $rootScope.cp;
|
||||||
|
console.log('### SENDING publicKeyRing TO:', recipients||'All');
|
||||||
|
|
||||||
|
cp.send(recipients, {
|
||||||
|
type: 'publicKeyRing',
|
||||||
|
publicKeyRing: $rootScope.publicKeyRing.toObj(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var _handlePublicKeyRing = function(senderId, data, isInbound) {
|
||||||
|
var cp = $rootScope.cp;
|
||||||
|
_checkWallet(data.publicKeyRing.id);
|
||||||
|
var shouldSend = false;
|
||||||
|
|
||||||
|
var recipients, pkr = $rootScope.publicKeyRing;
|
||||||
|
var inPKR = copay.PublicKeyRing.fromObj(data.publicKeyRing);
|
||||||
|
console.log('[network.js.176:inPKR:]',inPKR); //TODO
|
||||||
|
console.log('[network.js.178:pkr:]',pkr); //TODO
|
||||||
|
if (pkr.merge(inPKR, true) && !data.isBroadcast) {
|
||||||
|
console.log('### BROADCASTING PKR');
|
||||||
|
recipients = null;
|
||||||
|
shouldSend = true;
|
||||||
|
}
|
||||||
|
else if (isInbound && !data.isBroadcast) {
|
||||||
|
// always replying to connecting peer
|
||||||
|
console.log('### REPLYING PKR TO:', senderId);
|
||||||
|
recipients = senderId;
|
||||||
|
shouldSend = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[network.js.189:shouldSend:]',shouldSend); //TODO
|
||||||
|
if (shouldSend) {
|
||||||
|
sendPublicKeyRing(recipients);
|
||||||
|
}
|
||||||
|
_refreshUx();
|
||||||
|
};
|
||||||
|
var _handleTxProposals = function(senderId, data, isInbound) {
|
||||||
|
var cp = $rootScope.cp;
|
||||||
|
_checkWallet(data.txProposals.walletId);
|
||||||
|
|
||||||
|
var shouldSend = false;
|
||||||
|
console.log('RECV TXPROPOSAL:',data); //TODO
|
||||||
|
var recipients, pkr = $rootScope.txProposals;
|
||||||
|
|
||||||
|
var inTxProposals = copay.TxProposals.fromObj(data.txProposals);
|
||||||
|
var mergeInfo = pkr.merge(inTxProposals, true);
|
||||||
|
if ( mergeInfo.merged && !data.isBroadcast) {
|
||||||
|
console.log('### BROADCASTING txProposals');
|
||||||
|
recipients = null;
|
||||||
|
shouldSend = true;
|
||||||
|
}
|
||||||
|
else if (isInbound && !data.isBroadcast) {
|
||||||
|
// always replying to connecting peer
|
||||||
|
console.log('### REPLYING txProposals TO:', senderId);
|
||||||
|
recipients = senderId;
|
||||||
|
shouldSend = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldSend) {
|
||||||
|
sendTxProposals(recipients);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var _handleData = function(senderId, data, isInbound) {
|
||||||
|
|
||||||
switch(data.type) {
|
switch(data.type) {
|
||||||
case 'publicKeyRing':
|
case 'publicKeyRing':
|
||||||
_checkWallet(data.publicKeyRing.id);
|
_handlePublicKeyRing(senderId, data, isInbound);
|
||||||
var shouldSend = false;
|
break;
|
||||||
|
case 'txProposals':
|
||||||
var recipients, pkr = $rootScope.publicKeyRing;
|
_handleTxProposals(senderId, data, isInbound);
|
||||||
if (pkr.merge(data.publicKeyRing, true) && !data.isBroadcast) {
|
break;
|
||||||
console.log('### BROADCASTING PKR');
|
case 'abort':
|
||||||
recipients = null;
|
disconnect();
|
||||||
shouldSend = true;
|
|
||||||
}
|
|
||||||
else if (isInbound && !data.isBroadcast) {
|
|
||||||
// always replying to connecting peer
|
|
||||||
console.log('### REPLYING PKR TO:', senderId);
|
|
||||||
recipients = senderId;
|
|
||||||
shouldSend = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldSend) {
|
|
||||||
console.log('### SENDING PKR TO:', recipients);
|
|
||||||
cp.send( recipients, {
|
|
||||||
type: 'publicKeyRing',
|
|
||||||
publicKeyRing: $rootScope.publicKeyRing.toObj(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_refreshUx();
|
_refreshUx();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -133,7 +244,6 @@ angular.module('copay.network')
|
||||||
|
|
||||||
// public methods
|
// public methods
|
||||||
var init = function(cb) {
|
var init = function(cb) {
|
||||||
|
|
||||||
var cp = $rootScope.cp = new copay.CopayPeer({
|
var cp = $rootScope.cp = new copay.CopayPeer({
|
||||||
apiKey: config.p2pApiKey,
|
apiKey: config.p2pApiKey,
|
||||||
debug: config.p2pDebug,
|
debug: config.p2pDebug,
|
||||||
|
@ -141,7 +251,6 @@ angular.module('copay.network')
|
||||||
});
|
});
|
||||||
_setupHandlers();
|
_setupHandlers();
|
||||||
|
|
||||||
// inicia session
|
|
||||||
cp.start(function(peerId) {
|
cp.start(function(peerId) {
|
||||||
return cb();
|
return cb();
|
||||||
});
|
});
|
||||||
|
@ -151,28 +260,32 @@ angular.module('copay.network')
|
||||||
if ($rootScope.cp) {
|
if ($rootScope.cp) {
|
||||||
$rootScope.cp.disconnect();
|
$rootScope.cp.disconnect();
|
||||||
}
|
}
|
||||||
|
closeWallet();
|
||||||
Storage.remove('peerData');
|
Storage.remove('peerData');
|
||||||
$rootScope.isLogged = false;
|
$rootScope.isLogged = false;
|
||||||
_refreshUx();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var connect = function(peerId, openCallback, failCallBack) {
|
var connect = function(peerId, openCallback, failCallback) {
|
||||||
if ($rootScope.cp) {
|
if ($rootScope.cp) {
|
||||||
$rootScope.cp.connectTo(peerId, openCallback, function () {
|
$rootScope.cp.connectTo(peerId, openCallback, function () {
|
||||||
disconnect();
|
disconnect();
|
||||||
failCallBack();
|
failCallback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return failCallBack();
|
return failCallback();
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init: init,
|
init: init,
|
||||||
connect: connect,
|
connect: connect,
|
||||||
disconnect: disconnect,
|
disconnect: disconnect,
|
||||||
|
sendTxProposals: sendTxProposals,
|
||||||
|
|
||||||
|
// TODO Move to Wallet.
|
||||||
createWallet: createWallet,
|
createWallet: createWallet,
|
||||||
openWallet: openWallet,
|
openWallet: openWallet,
|
||||||
|
storeOpenWallet: storeOpenWallet,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,13 @@ angular.module('copay.storage')
|
||||||
},
|
},
|
||||||
addWalletId: function(walletId) {
|
addWalletId: function(walletId) {
|
||||||
var ids = localStorage.getItem('walletIds');
|
var ids = localStorage.getItem('walletIds');
|
||||||
|
if (ids) {
|
||||||
|
var list = ids.split(',');
|
||||||
|
var l = list.length;
|
||||||
|
for(var i=0; i<l; i++)
|
||||||
|
if (walletId === list[i])
|
||||||
|
return;
|
||||||
|
}
|
||||||
localStorage.setItem('walletIds', (ids?ids+',':'') + walletId);
|
localStorage.setItem('walletIds', (ids?ids+',':'') + walletId);
|
||||||
},
|
},
|
||||||
delWalletId: function(walletId) {
|
delWalletId: function(walletId) {
|
||||||
|
|
|
@ -3,12 +3,12 @@ var FakeStorage = function(){
|
||||||
this.storage = {};
|
this.storage = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
FakeStorage.prototype.set = function (id) {
|
FakeStorage.prototype.set = function (id, payload) {
|
||||||
return this.storage[id];
|
this.storage[id] = payload;
|
||||||
};
|
};
|
||||||
|
|
||||||
FakeStorage.prototype.get = function(id, payload) {
|
FakeStorage.prototype.get = function(id) {
|
||||||
this.storage[id] = payload;
|
return this.storage[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = require('soop')(FakeStorage);
|
module.exports = require('soop')(FakeStorage);
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var chai = chai || require('chai');
|
||||||
|
var should = chai.should();
|
||||||
|
var bitcore = bitcore || require('bitcore');
|
||||||
|
var Transaction = bitcore.Transaction;
|
||||||
|
var buffertools = bitcore.buffertools;
|
||||||
|
var WalletKey = bitcore.WalletKey;
|
||||||
|
var Key = bitcore.Key;
|
||||||
|
var BIP32 = bitcore.BIP32;
|
||||||
|
var bignum = bitcore.bignum;
|
||||||
|
var networks = bitcore.networks;
|
||||||
|
var Address = bitcore.Address;
|
||||||
|
var BitcorePrivateKey = bitcore.PrivateKey;
|
||||||
|
var copay = copay || require('../copay');
|
||||||
|
var PrivateKey = copay.PrivateKey || require('../js/models/PrivateKey');
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
networkName:'livenet',
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('PrivateKey model', function() {
|
||||||
|
|
||||||
|
it('should create an instance', function () {
|
||||||
|
var w = new PrivateKey(config);
|
||||||
|
should.exist(w);
|
||||||
|
should.exist(w.BIP32);
|
||||||
|
should.exist(w.BIP32.derive);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should derive priv keys', function () {
|
||||||
|
var w = new PrivateKey(config);
|
||||||
|
for(var j=0; j<2; j++) {
|
||||||
|
for(var i=0; i<3; i++) {
|
||||||
|
var wk = w.get(i,j);
|
||||||
|
should.exist(wk);
|
||||||
|
var o=wk.storeObj();
|
||||||
|
should.exist(o);
|
||||||
|
should.exist(o.priv);
|
||||||
|
should.exist(o.pub);
|
||||||
|
should.exist(o.addr);
|
||||||
|
var a = new Address(o.addr);
|
||||||
|
a.isValid().should.equal(true);
|
||||||
|
(function() {
|
||||||
|
var p = new PrivateKey(o.priv)
|
||||||
|
}).should.not.throw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('should derive priv keys array', function () {
|
||||||
|
var w = new PrivateKey(config);
|
||||||
|
var wks = w.getAll(2,3);
|
||||||
|
wks.length.should.equal(5);
|
||||||
|
for(var j=0; j<wks.length; j++) {
|
||||||
|
var wk = wks[j];
|
||||||
|
should.exist(wk);
|
||||||
|
var o=wk.storeObj();
|
||||||
|
should.exist(o);
|
||||||
|
should.exist(o.priv);
|
||||||
|
should.exist(o.pub);
|
||||||
|
should.exist(o.addr);
|
||||||
|
var a = new Address(o.addr);
|
||||||
|
a.isValid().should.equal(true);
|
||||||
|
(function() {
|
||||||
|
var p = new PrivateKey(o.priv)
|
||||||
|
}).should.not.throw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should calculate .id', function () {
|
||||||
|
var w1 = new PrivateKey(config);
|
||||||
|
should.exist(w1.id);
|
||||||
|
w1.id.length.should.equal(40);
|
||||||
|
});
|
||||||
|
it('fromObj toObj roundtrip', function () {
|
||||||
|
var w1 = new PrivateKey(config);
|
||||||
|
var w2 = PrivateKey.fromObj(w1.toObj());
|
||||||
|
|
||||||
|
w2.getBIP32().extendedPrivateKeyString().should.equal(w1.getBIP32().extendedPrivateKeyString());
|
||||||
|
w2.getBIP32().extendedPublicKeyString().should.equal(w1.getBIP32().extendedPublicKeyString());
|
||||||
|
w2.id.should.equal(w1.id);
|
||||||
|
|
||||||
|
|
||||||
|
w2.getBIP32(1,1).extendedPrivateKeyString().should
|
||||||
|
.equal(w1.getBIP32(1,1).extendedPrivateKeyString());
|
||||||
|
w2.getBIP32(1,0).extendedPrivateKeyString().should
|
||||||
|
.equal(w1.getBIP32(1,0).extendedPrivateKeyString());
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
|
@ -6,8 +6,7 @@ var bitcore = bitcore || require('bitcore');
|
||||||
var Address = bitcore.Address;
|
var Address = bitcore.Address;
|
||||||
var buffertools = bitcore.buffertools;
|
var buffertools = bitcore.buffertools;
|
||||||
var copay = copay || require('../copay');
|
var copay = copay || require('../copay');
|
||||||
var fakeStorage = copay.FakeStorage;
|
var PublicKeyRing = copay.PublicKeyRing;
|
||||||
var PublicKeyRing = copay.PublicKeyRing || require('soop').load('../js/models/PublicKeyRing', {Storage: fakeStorage});
|
|
||||||
|
|
||||||
var aMasterPubKey = 'tprv8ZgxMBicQKsPdSVTiWXEqCCzqRaRr9EAQdn5UVMpT9UHX67Dh1FmzEMbavPumpAicsUm2XvC6NTdcWB89yN5DUWx5HQ7z3KByUg7Ht74VRZ';
|
var aMasterPubKey = 'tprv8ZgxMBicQKsPdSVTiWXEqCCzqRaRr9EAQdn5UVMpT9UHX67Dh1FmzEMbavPumpAicsUm2XvC6NTdcWB89yN5DUWx5HQ7z3KByUg7Ht74VRZ';
|
||||||
|
|
||||||
|
@ -17,7 +16,6 @@ var config = {
|
||||||
};
|
};
|
||||||
|
|
||||||
var createW = function (networkName) {
|
var createW = function (networkName) {
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
networkName: networkName || 'livenet',
|
networkName: networkName || 'livenet',
|
||||||
};
|
};
|
||||||
|
@ -27,7 +25,7 @@ var createW = function (networkName) {
|
||||||
|
|
||||||
var copayers = [];
|
var copayers = [];
|
||||||
for(var i=0; i<5; i++) {
|
for(var i=0; i<5; i++) {
|
||||||
w.haveAllRequiredPubKeys().should.equal(false);
|
w.isComplete().should.equal(false);
|
||||||
var newEpk = w.addCopayer();
|
var newEpk = w.addCopayer();
|
||||||
copayers.push(newEpk);
|
copayers.push(newEpk);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +53,7 @@ describe('PublicKeyRing model', function() {
|
||||||
should.exist(w2);
|
should.exist(w2);
|
||||||
|
|
||||||
w2.registeredCopayers().should.equal(0);
|
w2.registeredCopayers().should.equal(0);
|
||||||
w2.haveAllRequiredPubKeys().should.equal(false);
|
w2.isComplete().should.equal(false);
|
||||||
|
|
||||||
w2.getAddress.bind(false).should.throw();
|
w2.getAddress.bind(false).should.throw();
|
||||||
});
|
});
|
||||||
|
@ -65,13 +63,13 @@ describe('PublicKeyRing model', function() {
|
||||||
var w = k.w;
|
var w = k.w;
|
||||||
var copayers = k.copayers;
|
var copayers = k.copayers;
|
||||||
|
|
||||||
w.haveAllRequiredPubKeys().should.equal(true);
|
w.isComplete().should.equal(true);
|
||||||
w.addCopayer.bind().should.throw();
|
w.addCopayer.bind().should.throw();
|
||||||
for(var i =0; i<5; i++)
|
for(var i =0; i<5; i++)
|
||||||
w.addCopayer.bind(copayers[i]).should.throw();
|
w.addCopayer.bind(copayers[i]).should.throw();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('show be able to store and retrieve', function () {
|
it('show be able to tostore and read', function () {
|
||||||
var k = createW();
|
var k = createW();
|
||||||
var w = k.w;
|
var w = k.w;
|
||||||
var copayers = k.copayers;
|
var copayers = k.copayers;
|
||||||
|
@ -80,13 +78,13 @@ describe('PublicKeyRing model', function() {
|
||||||
for(var i=0; i<5; i++)
|
for(var i=0; i<5; i++)
|
||||||
w.generateAddress(false);
|
w.generateAddress(false);
|
||||||
|
|
||||||
w.store().should.equal(true);
|
var data = w.toStore();
|
||||||
var ID = w.id;
|
should.exist(data);
|
||||||
delete w['id'];
|
|
||||||
w.store.bind().should.throw();
|
|
||||||
|
|
||||||
var w2 = PublicKeyRing.read(ID);
|
var ID = w.id;
|
||||||
w2.haveAllRequiredPubKeys().should.equal(true);
|
|
||||||
|
var w2 = PublicKeyRing.read(data, ID, 'dummy' );
|
||||||
|
w2.isComplete().should.equal(true);
|
||||||
w2.addCopayer.bind().should.throw();
|
w2.addCopayer.bind().should.throw();
|
||||||
for(var i =0; i<5; i++)
|
for(var i =0; i<5; i++)
|
||||||
w2.addCopayer.bind(copayers[i]).should.throw();
|
w2.addCopayer.bind(copayers[i]).should.throw();
|
||||||
|
@ -102,14 +100,13 @@ describe('PublicKeyRing model', function() {
|
||||||
|
|
||||||
for(var isChange=0; isChange<2; isChange++) {
|
for(var isChange=0; isChange<2; isChange++) {
|
||||||
for(var i=0; i<5; i++) {
|
for(var i=0; i<5; i++) {
|
||||||
var addr = w.generateAddress(isChange);
|
var a = w.generateAddress(isChange);
|
||||||
var a = new Address(addr);
|
|
||||||
a.isValid().should.equal(true);
|
a.isValid().should.equal(true);
|
||||||
a.isScript().should.equal(true);
|
a.isScript().should.equal(true);
|
||||||
a.network().name.should.equal('livenet');
|
a.network().name.should.equal('livenet');
|
||||||
if (i>1) {
|
if (i>1) {
|
||||||
w.getAddress(i-1,isChange).should
|
w.getAddress(i-1,isChange).toString().should
|
||||||
.not.equal(w.getAddress(i-2,isChange));
|
.not.equal(w.getAddress(i-2,isChange).toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +127,7 @@ describe('PublicKeyRing model', function() {
|
||||||
var as = w.getAddresses();
|
var as = w.getAddresses();
|
||||||
as.length.should.equal(12);
|
as.length.should.equal(12);
|
||||||
for(var j in as) {
|
for(var j in as) {
|
||||||
var a = new Address(as[j]);
|
var a = as[j];
|
||||||
a.isValid().should.equal(true);
|
a.isValid().should.equal(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -161,69 +158,71 @@ describe('PublicKeyRing model', function() {
|
||||||
networkName: 'livenet',
|
networkName: 'livenet',
|
||||||
id: w.id,
|
id: w.id,
|
||||||
});
|
});
|
||||||
w2.merge(w.toObj()).should.equal(true);
|
w2.merge(w).should.equal(true);
|
||||||
w2.requiredCopayers.should.equal(3);
|
w2.requiredCopayers.should.equal(3);
|
||||||
w2.totalCopayers.should.equal(5);
|
w2.totalCopayers.should.equal(5);
|
||||||
w2.changeAddressIndex.should.equal(2);
|
w2.changeAddressIndex.should.equal(2);
|
||||||
w2.addressIndex.should.equal(3);
|
w2.addressIndex.should.equal(3);
|
||||||
|
|
||||||
//
|
//
|
||||||
w2.merge(w.toObj()).should.equal(false);
|
w2.merge(w).should.equal(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('#merge check tests', function () {
|
it('#merge check tests', function () {
|
||||||
var k = createW();
|
var config = {
|
||||||
var w = k.w;
|
|
||||||
|
|
||||||
for(var i=0; i<2; i++)
|
|
||||||
w.generateAddress(true);
|
|
||||||
for(var i=0; i<3; i++)
|
|
||||||
w.generateAddress(false);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var w2 = new PublicKeyRing({
|
|
||||||
networkName: 'livenet',
|
networkName: 'livenet',
|
||||||
|
};
|
||||||
|
|
||||||
|
var w = new PublicKeyRing(config);
|
||||||
|
var w2 = new PublicKeyRing({
|
||||||
|
networkName: 'testnet', //wrong
|
||||||
|
id: w.id,
|
||||||
});
|
});
|
||||||
(function() { w2.merge(w.toObj());}).should.throw();
|
(function() { w2.merge(w);}).should.throw();
|
||||||
(function() { w2.merge(w,true);}).should.throw();
|
|
||||||
|
|
||||||
console.log('[test.publickeyring.js.190]'); //TODO
|
|
||||||
w2.merge(w.toObj(),true).should.equal(true);
|
|
||||||
|
|
||||||
console.log('[test.publickeyring.js.193]'); //TODO
|
|
||||||
|
|
||||||
|
|
||||||
var w3 = new PublicKeyRing({
|
var w3 = new PublicKeyRing({
|
||||||
networkName: 'livenet',
|
networkName: 'livenet',
|
||||||
id: w.id,
|
id: w.id,
|
||||||
requiredCopayers: 2,
|
requiredCopayers: 2, // wrong
|
||||||
});
|
});
|
||||||
(function() { w3.merge(w.toObj());}).should.throw();
|
(function() { w3.merge(w);}).should.throw();
|
||||||
|
|
||||||
var w4 = new PublicKeyRing({
|
var w4 = new PublicKeyRing({
|
||||||
networkName: 'testnet',
|
|
||||||
id: w.id,
|
|
||||||
});
|
|
||||||
(function() { w4.merge(w.toObj());}).should.throw();
|
|
||||||
|
|
||||||
var w5 = new PublicKeyRing({
|
|
||||||
networkName: 'livenet',
|
networkName: 'livenet',
|
||||||
id: w.id,
|
id: w.id,
|
||||||
totalCopayers: 4,
|
totalCopayers: 3, // wrong
|
||||||
});
|
});
|
||||||
(function() { w5.merge(w.toObj());}).should.throw();
|
(function() { w4.merge(w);}).should.throw();
|
||||||
|
|
||||||
|
|
||||||
var w6 = new PublicKeyRing({
|
var w6 = new PublicKeyRing({
|
||||||
networkName: 'livenet',
|
networkName: 'livenet',
|
||||||
id: w.id,
|
|
||||||
});
|
});
|
||||||
(function() { w6.merge(w);}).should.throw();
|
(function() { w6.merge(w);}).should.throw();
|
||||||
w.networkName= 'livenet';
|
w.networkName= 'livenet';
|
||||||
(function() { w6.merge(w);}).should.throw();
|
(function() { w6.merge(w);}).should.throw();
|
||||||
|
|
||||||
|
|
||||||
|
var w0 = new PublicKeyRing({
|
||||||
|
networkName: 'livenet',
|
||||||
|
});
|
||||||
|
w0.addCopayer();
|
||||||
|
w0.addCopayer();
|
||||||
|
w0.addCopayer();
|
||||||
|
w0.addCopayer();
|
||||||
|
w0.addCopayer();
|
||||||
|
(function() { w0.merge(w);}).should.throw();
|
||||||
|
w.merge(w0,true).should.equal(true);
|
||||||
|
w.isComplete().should.equal(true);
|
||||||
|
|
||||||
|
var wx = new PublicKeyRing({
|
||||||
|
networkName: 'livenet',
|
||||||
|
});
|
||||||
|
wx.addCopayer();
|
||||||
|
(function() { w.merge(wx, true);}).should.throw();
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -232,7 +231,7 @@ console.log('[test.publickeyring.js.193]'); //TODO
|
||||||
should.exist(w);
|
should.exist(w);
|
||||||
var copayers = [];
|
var copayers = [];
|
||||||
for(var i=0; i<2; i++) {
|
for(var i=0; i<2; i++) {
|
||||||
w.haveAllRequiredPubKeys().should.equal(false);
|
w.isComplete().should.equal(false);
|
||||||
w.addCopayer();
|
w.addCopayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,17 +242,17 @@ console.log('[test.publickeyring.js.193]'); //TODO
|
||||||
should.exist(w);
|
should.exist(w);
|
||||||
var copayers = [];
|
var copayers = [];
|
||||||
for(var i=0; i<3; i++) {
|
for(var i=0; i<3; i++) {
|
||||||
w2.haveAllRequiredPubKeys().should.equal(false);
|
w2.isComplete().should.equal(false);
|
||||||
w2.addCopayer();
|
w2.addCopayer();
|
||||||
}
|
}
|
||||||
w2.merge(w.toObj()).should.equal(true);
|
w2.merge(w).should.equal(true);
|
||||||
w2.haveAllRequiredPubKeys().should.equal(true);
|
w2.isComplete().should.equal(true);
|
||||||
w2.merge(w.toObj()).should.equal(false);
|
w2.merge(w).should.equal(false);
|
||||||
|
|
||||||
w.haveAllRequiredPubKeys().should.equal(false);
|
w.isComplete().should.equal(false);
|
||||||
w.merge(w2.toObj()).should.equal(true);
|
w.merge(w2).should.equal(true);
|
||||||
w.haveAllRequiredPubKeys().should.equal(true);
|
w.isComplete().should.equal(true);
|
||||||
w.merge(w2.toObj()).should.equal(false);
|
w.merge(w2).should.equal(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#merge pubkey tests (case 2)', function () {
|
it('#merge pubkey tests (case 2)', function () {
|
||||||
|
@ -261,16 +260,34 @@ console.log('[test.publickeyring.js.193]'); //TODO
|
||||||
should.exist(w);
|
should.exist(w);
|
||||||
|
|
||||||
for(var i=0; i<5; i++) {
|
for(var i=0; i<5; i++) {
|
||||||
w.haveAllRequiredPubKeys().should.equal(false);
|
w.isComplete().should.equal(false);
|
||||||
var w2 = new PublicKeyRing({
|
var w2 = new PublicKeyRing({
|
||||||
networkName: 'livenet',
|
networkName: 'livenet',
|
||||||
id: w.id,
|
id: w.id,
|
||||||
});
|
});
|
||||||
w2.addCopayer();
|
w2.addCopayer();
|
||||||
w.merge(w2.toObj()).should.equal(true);
|
w.merge(w2).should.equal(true);
|
||||||
}
|
}
|
||||||
w.haveAllRequiredPubKeys().should.equal(true);
|
w.isComplete().should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('#getRedeemScriptMap check tests', function () {
|
||||||
|
var k = createW();
|
||||||
|
var w = k.w;
|
||||||
|
|
||||||
|
for(var i=0; i<2; i++)
|
||||||
|
w.generateAddress(true);
|
||||||
|
for(var i=0; i<3; i++)
|
||||||
|
w.generateAddress(false);
|
||||||
|
|
||||||
|
var m = w.getRedeemScriptMap();
|
||||||
|
Object.keys(m).length.should.equal(5);
|
||||||
|
Object.keys(m).forEach(function (k) {
|
||||||
|
should.exist(m[k]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,507 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var chai = chai || require('chai');
|
||||||
|
var should = chai.should();
|
||||||
|
var bitcore = bitcore || require('bitcore');
|
||||||
|
var Transaction = bitcore.Transaction;
|
||||||
|
var buffertools = bitcore.buffertools;
|
||||||
|
var WalletKey = bitcore.WalletKey;
|
||||||
|
var Key = bitcore.Key;
|
||||||
|
var BIP32 = bitcore.BIP32;
|
||||||
|
var bignum = bitcore.bignum;
|
||||||
|
var Script = bitcore.Script;
|
||||||
|
var util = bitcore.util;
|
||||||
|
var networks = bitcore.networks;
|
||||||
|
var copay = copay || require('../copay');
|
||||||
|
var fakeStorage = copay.FakeStorage;
|
||||||
|
var PrivateKey = copay.PrivateKey || require('../js/models/PrivateKey');
|
||||||
|
var TxProposals = copay.TxProposals || require('../js/models/TxProposal');
|
||||||
|
var PublicKeyRing = (typeof process.versions === 'undefined') ? copay.PublicKeyRing :
|
||||||
|
require('soop').load('../js/models/PublicKeyRing', {Storage: fakeStorage});
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
networkName:'livenet',
|
||||||
|
};
|
||||||
|
|
||||||
|
var unspentTest = [
|
||||||
|
{
|
||||||
|
"address": "dummy",
|
||||||
|
"scriptPubKey": "dummy",
|
||||||
|
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
|
||||||
|
"vout": 1,
|
||||||
|
"amount": 10,
|
||||||
|
"confirmations":7
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
var createPKR = function (bip32s) {
|
||||||
|
var w = new PublicKeyRing(config);
|
||||||
|
should.exist(w);
|
||||||
|
|
||||||
|
for(var i=0; i<5; i++) {
|
||||||
|
if (bip32s) {
|
||||||
|
var b=bip32s[i];
|
||||||
|
w.addCopayer(b?b.extendedPublicKeyString():null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
w.addCopayer();
|
||||||
|
}
|
||||||
|
w.generateAddress(true);
|
||||||
|
w.generateAddress(true);
|
||||||
|
w.generateAddress(true);
|
||||||
|
w.generateAddress(false);
|
||||||
|
w.generateAddress(false);
|
||||||
|
w.generateAddress(false);
|
||||||
|
//3x3 indexes
|
||||||
|
|
||||||
|
return w;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
describe('TxProposals model', function() {
|
||||||
|
|
||||||
|
it('should create an instance', function () {
|
||||||
|
var w = new TxProposals({
|
||||||
|
networkName: config.networkName
|
||||||
|
});
|
||||||
|
should.exist(w);
|
||||||
|
w.network.name.should.equal('livenet');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#create, no signing', function () {
|
||||||
|
var w = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: createPKR(),
|
||||||
|
});
|
||||||
|
should.exist(w);
|
||||||
|
w.network.name.should.equal('livenet');
|
||||||
|
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(1, true).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true);
|
||||||
|
|
||||||
|
w.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest
|
||||||
|
);
|
||||||
|
var tx = w.txps[0].builder.build();
|
||||||
|
should.exist(tx);
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
Object.keys(w.txps[0].signedBy).length.should.equal(0);
|
||||||
|
Object.keys(w.txps[0].seenBy).length.should.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('#create, signing with wrong key', function () {
|
||||||
|
var w = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: createPKR(),
|
||||||
|
});
|
||||||
|
should.exist(w);
|
||||||
|
w.network.name.should.equal('livenet');
|
||||||
|
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(1, true).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true);
|
||||||
|
|
||||||
|
var priv = new PrivateKey(config);
|
||||||
|
w.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest,
|
||||||
|
priv
|
||||||
|
);
|
||||||
|
var tx = w.txps[0].builder.build();
|
||||||
|
should.exist(tx);
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
Object.keys(w.txps[0].signedBy).length.should.equal(0);
|
||||||
|
Object.keys(w.txps[0].seenBy).length.should.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('#create. Signing with derivate keys', function () {
|
||||||
|
|
||||||
|
var priv = new PrivateKey(config);
|
||||||
|
var w = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: createPKR([priv.getBIP32()]),
|
||||||
|
});
|
||||||
|
|
||||||
|
var ts = Date.now();
|
||||||
|
for (var isChange=0; isChange<2; isChange++) {
|
||||||
|
for (var index=0; index<3; index++) {
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest,
|
||||||
|
priv
|
||||||
|
);
|
||||||
|
var tx = w.txps[0].builder.build();
|
||||||
|
should.exist(tx);
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#merge with self', function () {
|
||||||
|
|
||||||
|
var priv = new PrivateKey(config);
|
||||||
|
var w = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: createPKR([priv.getBIP32()]),
|
||||||
|
});
|
||||||
|
var ts = Date.now();
|
||||||
|
var isChange=0;
|
||||||
|
var index=0;
|
||||||
|
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest,
|
||||||
|
priv
|
||||||
|
);
|
||||||
|
var tx = w.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
w.merge(w);
|
||||||
|
w.txps.length.should.equal(1);
|
||||||
|
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
it('#merge, merge signatures case 1', function () {
|
||||||
|
|
||||||
|
var priv2 = new PrivateKey(config);
|
||||||
|
var priv = new PrivateKey(config);
|
||||||
|
var ts = Date.now();
|
||||||
|
var isChange=0;
|
||||||
|
var index=0;
|
||||||
|
var pkr = createPKR([priv.getBIP32()]);
|
||||||
|
var opts = {remainderOut: { address: pkr.generateAddress(true).toString() }};
|
||||||
|
|
||||||
|
|
||||||
|
var w = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest,
|
||||||
|
priv2,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
|
||||||
|
var tx = w.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(1);
|
||||||
|
|
||||||
|
Object.keys(w.txps[0].signedBy).length.should.equal(0);
|
||||||
|
Object.keys(w.txps[0].seenBy).length.should.equal(1);
|
||||||
|
|
||||||
|
|
||||||
|
var w2 = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: w.publicKeyRing,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w2.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest,
|
||||||
|
priv,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
|
||||||
|
var tx = w2.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
|
(w2.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w2.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
w.merge(w2);
|
||||||
|
w.txps.length.should.equal(1);
|
||||||
|
|
||||||
|
var tx = w.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
var _dumpChunks = function (scriptSig, label) {
|
||||||
|
console.log('## DUMP: ' + label + ' ##');
|
||||||
|
for(var i=0; i<scriptSig.chunks.length; i++) {
|
||||||
|
console.log('\tCHUNK ', i, scriptSig.chunks[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
it('#merge, merge signatures case 2', function () {
|
||||||
|
|
||||||
|
var priv = new PrivateKey(config);
|
||||||
|
var priv2 = new PrivateKey(config);
|
||||||
|
var priv3 = new PrivateKey(config);
|
||||||
|
var ts = Date.now();
|
||||||
|
var isChange=0;
|
||||||
|
var index=0;
|
||||||
|
var pkr = createPKR([priv.getBIP32(), priv2.getBIP32()]);
|
||||||
|
var opts = {remainderOut: { address: pkr.generateAddress(true).toString() }};
|
||||||
|
|
||||||
|
var w = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest,
|
||||||
|
priv3,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
|
||||||
|
var tx = w.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(1);
|
||||||
|
|
||||||
|
Object.keys(w.txps[0].signedBy).length.should.equal(0);
|
||||||
|
Object.keys(w.txps[0].seenBy).length.should.equal(1);
|
||||||
|
|
||||||
|
|
||||||
|
var w2 = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w2.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest,
|
||||||
|
priv,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
tx = w2.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
|
(w2.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w2.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
w.merge(w2);
|
||||||
|
w.txps.length.should.equal(1);
|
||||||
|
|
||||||
|
tx = w.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
|
||||||
|
var w3 = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w3.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest,
|
||||||
|
priv2,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
tx = w3.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
|
(w3.txps[0].signedBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
(w3.txps[0].seenBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
w.merge(w3);
|
||||||
|
w.txps.length.should.equal(1);
|
||||||
|
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].signedBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
tx = w.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('#merge, merge signatures case 3', function () {
|
||||||
|
|
||||||
|
var priv = new PrivateKey(config);
|
||||||
|
var priv2 = new PrivateKey(config);
|
||||||
|
var priv3 = new PrivateKey(config);
|
||||||
|
var ts = Date.now();
|
||||||
|
var isChange=0;
|
||||||
|
var index=0;
|
||||||
|
var pkr = createPKR([priv.getBIP32(), priv2.getBIP32(), priv3.getBIP32() ]);
|
||||||
|
var opts = {remainderOut: { address: pkr.generateAddress(true).toString() }};
|
||||||
|
|
||||||
|
var w = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest,
|
||||||
|
priv,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
var tx = w.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
|
||||||
|
var w2 = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w2.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest,
|
||||||
|
priv2,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
var tx = w2.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
(w2.txps[0].signedBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
(w2.txps[0].seenBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
|
||||||
|
var w3 = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w3.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest,
|
||||||
|
priv3,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
var tx = w3.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
(w3.txps[0].signedBy[priv3.id] - ts > 0).should.equal(true);
|
||||||
|
(w3.txps[0].seenBy[priv3.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
w.merge(w2);
|
||||||
|
w.txps.length.should.equal(1);
|
||||||
|
var tx = w.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(1);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].signedBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
|
||||||
|
w.merge(w3);
|
||||||
|
var tx = w.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(true);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(0);
|
||||||
|
w.txps.length.should.equal(1);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv3.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].signedBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].signedBy[priv3.id] - ts > 0).should.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
it('#toObj #fromObj roundtrip', function () {
|
||||||
|
|
||||||
|
var priv = new PrivateKey(config);
|
||||||
|
var w = new TxProposals({
|
||||||
|
walletId: 'qwerty',
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: createPKR([priv.getBIP32()]),
|
||||||
|
});
|
||||||
|
var ts = Date.now();
|
||||||
|
var isChange=0;
|
||||||
|
var index=0;
|
||||||
|
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest,
|
||||||
|
priv
|
||||||
|
);
|
||||||
|
var tx = w.txps[0].builder.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
var o = w.toObj();
|
||||||
|
should.exist(o);
|
||||||
|
o.txps.length.should.equal(1);
|
||||||
|
should.exist(o.txps[0]);
|
||||||
|
should.exist(o.txps[0].signedBy);
|
||||||
|
should.exist(o.txps[0].seenBy);
|
||||||
|
should.exist(o.txps[0].builderObj);
|
||||||
|
should.exist(o.txps[0].builderObj.valueInSat);
|
||||||
|
should.exist(o.txps[0].signedBy[priv.id]);
|
||||||
|
|
||||||
|
var w2 = TxProposals.fromObj(o);
|
||||||
|
w2.walletId.should.equal(w.walletId);
|
||||||
|
var tx2 = w2.txps[0].builder.build();
|
||||||
|
tx2.isComplete().should.equal(false);
|
||||||
|
tx2.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
(w2.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w2.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
should.exist(w2.txps[0].builder);
|
||||||
|
should.exist(w2.txps[0].builder.valueInSat);
|
||||||
|
|
||||||
|
w2.merge(w);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue