mirror of https://github.com/BTCPrivate/copay.git
fix conflics
This commit is contained in:
commit
44901cd635
16
.travis.yml
16
.travis.yml
|
@ -1,6 +1,14 @@
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- "0.10"
|
- '0.10'
|
||||||
before_install: "npm install -g grunt-cli"
|
before_install: npm install -g grunt-cli
|
||||||
install: "npm install"
|
install: npm install
|
||||||
before_script: "grunt shell"
|
before_script: grunt shell
|
||||||
|
notifications:
|
||||||
|
hipchat:
|
||||||
|
rooms:
|
||||||
|
secure: ibsyaK2/7hdms9pRHCibamfUtA1yb5Ib3tIQOChwnfNSkpOy5ZViuq92duPZpKacgNuRmiwp6wfbDY8duLiadwnCet4V3dHAGqt+t9o+O7A9DzxNeYFFHnnrhqhg95PjWK6UyFqrQajPIr+n11M2itEQLa7kljQxJZ3Z26zJCdI=
|
||||||
|
template:
|
||||||
|
- '%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message} (<a href="%{build_url}">Details</a>/<a href="%{compare_url}">Change view</a>)'
|
||||||
|
format: html
|
||||||
|
on_success: never
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('HeaderController',
|
angular.module('copayApp.controllers').controller('HeaderController',
|
||||||
function($scope, $rootScope, $location, $notification, $http, controllerUtils) {
|
function($scope, $rootScope, $location, notification, $http, controllerUtils) {
|
||||||
$scope.menu = [
|
$scope.menu = [
|
||||||
{
|
{
|
||||||
'title': 'Addresses',
|
'title': 'Addresses',
|
||||||
|
@ -64,7 +64,7 @@ angular.module('copayApp.controllers').controller('HeaderController',
|
||||||
}
|
}
|
||||||
if (currentAddr) {
|
if (currentAddr) {
|
||||||
//var beep = new Audio('sound/transaction.mp3');
|
//var beep = new Audio('sound/transaction.mp3');
|
||||||
$notification.funds('Received fund', currentAddr, receivedFund);
|
notification.funds('Received fund', currentAddr, receivedFund);
|
||||||
//beep.play();
|
//beep.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ angular.module('copayApp.controllers').controller('HeaderController',
|
||||||
|
|
||||||
$rootScope.$watch('txAlertCount', function(txAlertCount) {
|
$rootScope.$watch('txAlertCount', function(txAlertCount) {
|
||||||
if (txAlertCount && txAlertCount > 0) {
|
if (txAlertCount && txAlertCount > 0) {
|
||||||
$notification.info('New Transaction', ($rootScope.txAlertCount == 1) ? 'You have a pending transaction proposal' : 'You have ' + $rootScope.txAlertCount + ' pending transaction proposals', txAlertCount);
|
notification.info('New Transaction', ($rootScope.txAlertCount == 1) ? 'You have a pending transaction proposal' : 'You have ' + $rootScope.txAlertCount + ' pending transaction proposals', txAlertCount);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,21 +6,16 @@ angular.module('copayApp.controllers').controller('ImportController',
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
var _importBackup = function(encryptedObj) {
|
var _importBackup = function(encryptedObj) {
|
||||||
Passphrase.getBase64Async($scope.password, function(passphrase){
|
Passphrase.getBase64Async($scope.password, function(passphrase){
|
||||||
var w, errMsg;
|
walletFactory.import(encryptedObj, passphrase, function(err, w) {
|
||||||
try {
|
if (err) {
|
||||||
w = walletFactory.fromEncryptedObj(encryptedObj, passphrase);
|
$scope.loading = false;
|
||||||
} catch(e) {
|
$rootScope.$flashMessage = { message: err.errMsg || 'Wrong password', type: 'error'};
|
||||||
errMsg = e.message;
|
$rootScope.$digest();
|
||||||
}
|
return;
|
||||||
if (!w) {
|
}
|
||||||
$scope.loading = false;
|
$rootScope.wallet = w;
|
||||||
$rootScope.$flashMessage = { message: errMsg || 'Wrong password', type: 'error'};
|
controllerUtils.startNetwork($rootScope.wallet, $scope);
|
||||||
$rootScope.$digest();
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
$rootScope.wallet = w;
|
|
||||||
|
|
||||||
controllerUtils.startNetwork($rootScope.wallet, $scope);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -163,11 +163,34 @@ Insight.prototype.sendRawTransaction = function(rawtx, cb) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Insight.prototype.checkActivity = function(addresses, cb) {
|
||||||
|
if (!addresses) throw new Error('address must be set');
|
||||||
|
|
||||||
|
this.getTransactions(addresses, function onResult(txs) {
|
||||||
|
var flatArray = function (xss) { return xss.reduce(function(r, xs) { return r.concat(xs); }, []); };
|
||||||
|
var getInputs = function (t) { return t.vin.map(function (vin) { return vin.addr }); };
|
||||||
|
var getOutputs = function (t) { return flatArray(
|
||||||
|
t.vout.map(function (vout) { return vout.scriptPubKey.addresses; })
|
||||||
|
);};
|
||||||
|
|
||||||
|
var activityMap = new Array(addresses.length);
|
||||||
|
var activeAddress = flatArray(txs.map(function(t) { return getInputs(t).concat(getOutputs(t)); }));
|
||||||
|
activeAddress.forEach(function (addr) {
|
||||||
|
var index = addresses.indexOf(addr);
|
||||||
|
if (index != -1) activityMap[index] = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
cb(null, activityMap);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Insight.prototype._requestNode = function(options, callback) {
|
Insight.prototype._requestNode = function(options, callback) {
|
||||||
if (options.method === 'POST') {
|
if (options.method === 'POST') {
|
||||||
options.headers = {
|
options.headers = {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
'Content-Length': options.data.length,
|
'Content-Length': options.data.length,
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ AddressIndex.prototype.toObj = function() {
|
||||||
AddressIndex.prototype.checkRange = function(index, isChange) {
|
AddressIndex.prototype.checkRange = function(index, isChange) {
|
||||||
if ((isChange && index > this.changeIndex) ||
|
if ((isChange && index > this.changeIndex) ||
|
||||||
(!isChange && index > this.receiveIndex)) {
|
(!isChange && index > this.receiveIndex)) {
|
||||||
throw new Error('Out of bounds at index %d isChange: %d', index, isChange);
|
throw new Error('Out of bounds at index ' + index + ' isChange: ' + isChange);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -157,8 +157,6 @@ PublicKeyRing.prototype.getPubKeys = function(index, isChange) {
|
||||||
|
|
||||||
// TODO this could be cached
|
// TODO this could be cached
|
||||||
PublicKeyRing.prototype.getRedeemScript = function (index, isChange) {
|
PublicKeyRing.prototype.getRedeemScript = function (index, isChange) {
|
||||||
this.indexes.checkRange(index, isChange);
|
|
||||||
|
|
||||||
var pubKeys = this.getPubKeys(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;
|
||||||
|
|
|
@ -22,6 +22,11 @@ function TxProposal(opts) {
|
||||||
this.comment = opts.comment || null;
|
this.comment = opts.comment || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TxProposal.prototype.getID = function() {
|
||||||
|
var ntxid = this.builder.build().getNormalizedHash().toString('hex');
|
||||||
|
return ntxid;
|
||||||
|
};
|
||||||
|
|
||||||
TxProposal.prototype.toObj = function() {
|
TxProposal.prototype.toObj = function() {
|
||||||
var o = JSON.parse(JSON.stringify(this));
|
var o = JSON.parse(JSON.stringify(this));
|
||||||
delete o['builder'];
|
delete o['builder'];
|
||||||
|
@ -46,6 +51,76 @@ TxProposal.getSentTs = function() {
|
||||||
return this.sentTs;
|
return this.sentTs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TxProposal.prototype.merge = function(other) {
|
||||||
|
var ret = {};
|
||||||
|
ret.events = this.mergeMetadata(other);
|
||||||
|
ret.hasChanged = this.mergeBuilder(other);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
TxProposal.prototype.mergeBuilder = function(other) {
|
||||||
|
var b0 = this.builder;
|
||||||
|
var b1 = other.builder;
|
||||||
|
|
||||||
|
// TODO: improve this comparison
|
||||||
|
var before = JSON.stringify(b0.toObj());
|
||||||
|
b0.merge(b1);
|
||||||
|
var after = JSON.stringify(b0.toObj());
|
||||||
|
return after !== before;
|
||||||
|
};
|
||||||
|
|
||||||
|
TxProposal.prototype.mergeMetadata = function(v1) {
|
||||||
|
var events = [];
|
||||||
|
var v0 = this;
|
||||||
|
|
||||||
|
var ntxid = this.getID();
|
||||||
|
|
||||||
|
Object.keys(v1.seenBy).forEach(function(k) {
|
||||||
|
if (!v0.seenBy[k]) {
|
||||||
|
v0.seenBy[k] = v1.seenBy[k];
|
||||||
|
events.push({
|
||||||
|
type: 'seen',
|
||||||
|
cId: k,
|
||||||
|
txId: ntxid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(v1.signedBy).forEach(function(k) {
|
||||||
|
if (!v0.signedBy[k]) {
|
||||||
|
v0.signedBy[k] = v1.signedBy[k];
|
||||||
|
events.push({
|
||||||
|
type: 'signed',
|
||||||
|
cId: k,
|
||||||
|
txId: ntxid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(v1.rejectedBy).forEach(function(k) {
|
||||||
|
if (!v0.rejectedBy[k]) {
|
||||||
|
v0.rejectedBy[k] = v1.rejectedBy[k];
|
||||||
|
events.push({
|
||||||
|
type: 'rejected',
|
||||||
|
cId: k,
|
||||||
|
txId: ntxid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!v0.sentTxid && v1.sentTxid) {
|
||||||
|
v0.sentTs = v1.sentTs;
|
||||||
|
v0.sentTxid = v1.sentTxid;
|
||||||
|
events.push({
|
||||||
|
type: 'broadcast',
|
||||||
|
txId: ntxid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return events;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = require('soop')(TxProposal);
|
module.exports = require('soop')(TxProposal);
|
||||||
|
|
||||||
|
|
||||||
|
@ -75,6 +150,7 @@ TxProposals.prototype.getNtxids = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
TxProposals.prototype.toObj = function(onlyThisNtxid) {
|
TxProposals.prototype.toObj = function(onlyThisNtxid) {
|
||||||
|
if (onlyThisNtxid) throw new Error();
|
||||||
var ret = [];
|
var ret = [];
|
||||||
for (var id in this.txps) {
|
for (var id in this.txps) {
|
||||||
|
|
||||||
|
@ -92,114 +168,37 @@ TxProposals.prototype.toObj = function(onlyThisNtxid) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
TxProposals.prototype._startMerge = function(myTxps, theirTxps) {
|
TxProposals.prototype.merge = function(inTxp) {
|
||||||
var fromUs = 0,
|
var myTxps = this.txps;
|
||||||
fromTheirs = 0,
|
|
||||||
merged = 0;
|
|
||||||
var toMerge = {},
|
|
||||||
ready = {},
|
|
||||||
events = [];
|
|
||||||
|
|
||||||
for (var hash in theirTxps) {
|
var ntxid = inTxp.getID();
|
||||||
if (!myTxps[hash]) {
|
var ret = {};
|
||||||
ready[hash] = theirTxps[hash]; // only in theirs;
|
ret.events = [];
|
||||||
events.push({type: 'new', cid: theirTxps[hash].creator, tx: hash});
|
ret.events.hasChanged = false;
|
||||||
fromTheirs++;
|
|
||||||
} else {
|
|
||||||
toMerge[hash] = theirTxps[hash]; // need Merging
|
|
||||||
merged++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var hash in myTxps) {
|
if (myTxps[ntxid]) {
|
||||||
if (!toMerge[hash]) {
|
var v0 = myTxps[ntxid];
|
||||||
ready[hash] = myTxps[hash]; // only in myTxps;
|
var v1 = inTxp;
|
||||||
fromUs++;
|
ret = v0.merge(v1);
|
||||||
}
|
} else {
|
||||||
}
|
this.txps[ntxid] = inTxp;
|
||||||
|
ret.hasChanged = true;
|
||||||
return {
|
ret.events.push({
|
||||||
stats: {
|
type: 'new',
|
||||||
fromUs: fromUs,
|
cid: inTxp.creator,
|
||||||
fromTheirs: fromTheirs,
|
tx: ntxid
|
||||||
merged: merged,
|
|
||||||
},
|
|
||||||
ready: ready,
|
|
||||||
toMerge: toMerge,
|
|
||||||
events: events
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO add signatures.
|
|
||||||
TxProposals.prototype._mergeMetadata = function(myTxps, theirTxps, mergeInfo) {
|
|
||||||
|
|
||||||
var toMerge = mergeInfo.toMerge;
|
|
||||||
var hasChanged = 0;
|
|
||||||
var events = [];
|
|
||||||
|
|
||||||
Object.keys(toMerge).forEach(function(hash) {
|
|
||||||
var v0 = myTxps[hash];
|
|
||||||
var v1 = toMerge[hash];
|
|
||||||
|
|
||||||
Object.keys(v1.seenBy).forEach(function(k) {
|
|
||||||
if (!v0.seenBy[k]) {
|
|
||||||
v0.seenBy[k] = v1.seenBy[k];
|
|
||||||
events.push({type: 'seen', cId: k, txId: hash});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.keys(v1.signedBy).forEach(function(k) {
|
|
||||||
if (!v0.signedBy[k]) {
|
|
||||||
v0.signedBy[k] = v1.signedBy[k];
|
|
||||||
events.push({type: 'signed', cId: k, txId: hash});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.keys(v1.rejectedBy).forEach(function(k) {
|
|
||||||
if (!v0.rejectedBy[k]) {
|
|
||||||
v0.rejectedBy[k] = v1.rejectedBy[k];
|
|
||||||
events.push({type: 'rejected', cId: k, txId: hash});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!v0.sentTxid && v1.sentTxid) {
|
|
||||||
v0.sentTs = v1.sentTs;
|
|
||||||
v0.sentTxid = v1.sentTxid;
|
|
||||||
events.push({type: 'broadcast', txId: hash});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
events: events.concat(mergeInfo.events),
|
|
||||||
hasChanged: events.length
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
TxProposals.prototype._mergeBuilder = function(myTxps, theirTxps, mergeInfo) {
|
|
||||||
var toMerge = mergeInfo.toMerge;
|
|
||||||
var hasChanged = 0;
|
|
||||||
|
|
||||||
for (var hash in toMerge) {
|
|
||||||
var v0 = myTxps[hash].builder;
|
|
||||||
var v1 = toMerge[hash].builder;
|
|
||||||
|
|
||||||
// TODO: enhance this
|
|
||||||
var before = JSON.stringify(v0.toObj());
|
|
||||||
v0.merge(v1);
|
|
||||||
var after = JSON.stringify(v0.toObj());
|
|
||||||
if (after !== before) hasChanged++;
|
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
TxProposals.prototype.add = function(data) {
|
TxProposals.prototype.add = function(data) {
|
||||||
var ntxid = data.builder.build().getNormalizedHash().toString('hex');
|
var txp = new TxProposal(data);
|
||||||
this.txps[ntxid] = new TxProposal(data);
|
var ntxid = txp.getID();
|
||||||
|
this.txps[ntxid] = txp;
|
||||||
return ntxid;
|
return ntxid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
TxProposals.prototype.setSent = function(ntxid, txid) {
|
TxProposals.prototype.setSent = function(ntxid, txid) {
|
||||||
//sent TxProposals are local an not broadcasted.
|
//sent TxProposals are local an not broadcasted.
|
||||||
this.txps[ntxid].setSent(txid);
|
this.txps[ntxid].setSent(txid);
|
||||||
|
@ -259,26 +258,5 @@ TxProposals.prototype.getUsedUnspent = function(maxRejectCount) {
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
TxProposals.prototype.merge = function(t) {
|
TxProposals.TxProposal = TxProposal;
|
||||||
if (this.network.name !== t.network.name)
|
|
||||||
throw new Error('network mismatch in:', t);
|
|
||||||
|
|
||||||
var myTxps = this.txps;
|
|
||||||
var theirTxps = t.txps;
|
|
||||||
|
|
||||||
var mergeInfo = this._startMerge(myTxps, theirTxps);
|
|
||||||
var result = this._mergeMetadata(myTxps, theirTxps, mergeInfo);
|
|
||||||
result.hasChanged += this._mergeBuilder(myTxps, theirTxps, mergeInfo);
|
|
||||||
|
|
||||||
Object.keys(mergeInfo.toMerge).forEach(function(hash) {
|
|
||||||
mergeInfo.ready[hash] = myTxps[hash];
|
|
||||||
});
|
|
||||||
|
|
||||||
mergeInfo.stats.hasChanged = result.hasChanged;
|
|
||||||
mergeInfo.stats.events = result.events;
|
|
||||||
|
|
||||||
this.txps = mergeInfo.ready;
|
|
||||||
return mergeInfo.stats;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = require('soop')(TxProposals);
|
module.exports = require('soop')(TxProposals);
|
||||||
|
|
|
@ -2,17 +2,24 @@
|
||||||
|
|
||||||
var imports = require('soop').imports();
|
var imports = require('soop').imports();
|
||||||
|
|
||||||
|
var http = require('http');
|
||||||
|
var EventEmitter = imports.EventEmitter || require('events').EventEmitter;
|
||||||
|
var async = require('async');
|
||||||
|
var preconditions = require('preconditions').instance();
|
||||||
|
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var bignum = bitcore.Bignum;
|
var bignum = bitcore.Bignum;
|
||||||
var coinUtil = bitcore.util;
|
var coinUtil = bitcore.util;
|
||||||
var buffertools = bitcore.buffertools;
|
var buffertools = bitcore.buffertools;
|
||||||
var Builder = bitcore.TransactionBuilder;
|
var Builder = bitcore.TransactionBuilder;
|
||||||
var http = require('http');
|
|
||||||
var EventEmitter = imports.EventEmitter || require('events').EventEmitter;
|
|
||||||
var copay = copay || require('../../../copay');
|
|
||||||
var SecureRandom = bitcore.SecureRandom;
|
var SecureRandom = bitcore.SecureRandom;
|
||||||
var Base58Check = bitcore.Base58.base58Check;
|
var Base58Check = bitcore.Base58.base58Check;
|
||||||
|
|
||||||
|
var AddressIndex = require('./AddressIndex');
|
||||||
|
var PublicKeyRing = require('./PublicKeyRing');
|
||||||
|
var TxProposals = require('./TxProposals');
|
||||||
|
var PrivateKey = require('./PrivateKey');
|
||||||
|
|
||||||
function Wallet(opts) {
|
function Wallet(opts) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -74,7 +81,7 @@ Wallet.prototype.connectToAll = function() {
|
||||||
|
|
||||||
Wallet.prototype._handleIndexes = function(senderId, data, isInbound) {
|
Wallet.prototype._handleIndexes = function(senderId, data, isInbound) {
|
||||||
this.log('RECV INDEXES:', data);
|
this.log('RECV INDEXES:', data);
|
||||||
var inIndexes = copay.AddressIndex.fromObj(data.indexes);
|
var inIndexes = AddressIndex.fromObj(data.indexes);
|
||||||
var hasChanged = this.publicKeyRing.indexes.merge(inIndexes);
|
var hasChanged = this.publicKeyRing.indexes.merge(inIndexes);
|
||||||
if (hasChanged) {
|
if (hasChanged) {
|
||||||
this.emit('publicKeyRingUpdated');
|
this.emit('publicKeyRingUpdated');
|
||||||
|
@ -85,7 +92,7 @@ Wallet.prototype._handleIndexes = function(senderId, data, isInbound) {
|
||||||
Wallet.prototype._handlePublicKeyRing = function(senderId, data, isInbound) {
|
Wallet.prototype._handlePublicKeyRing = function(senderId, data, isInbound) {
|
||||||
this.log('RECV PUBLICKEYRING:', data);
|
this.log('RECV PUBLICKEYRING:', data);
|
||||||
|
|
||||||
var inPKR = copay.PublicKeyRing.fromObj(data.publicKeyRing);
|
var inPKR = PublicKeyRing.fromObj(data.publicKeyRing);
|
||||||
var wasIncomplete = !this.publicKeyRing.isComplete();
|
var wasIncomplete = !this.publicKeyRing.isComplete();
|
||||||
var hasChanged;
|
var hasChanged;
|
||||||
|
|
||||||
|
@ -110,31 +117,17 @@ Wallet.prototype._handlePublicKeyRing = function(senderId, data, isInbound) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Wallet.prototype._handleTxProposals = function(senderId, data, isInbound) {
|
Wallet.prototype._handleTxProposal = function(senderId, data) {
|
||||||
this.log('RECV TXPROPOSAL:', data);
|
this.log('RECV TXPROPOSAL:', data);
|
||||||
|
|
||||||
var recipients;
|
var inTxp = TxProposals.TxProposal.fromObj(data.txProposal);
|
||||||
var inTxp = copay.TxProposals.fromObj(data.txProposals);
|
|
||||||
var ids = inTxp.getNtxids();
|
|
||||||
|
|
||||||
if (ids.length > 1) {
|
var mergeInfo = this.txProposals.merge(inTxp);
|
||||||
this.emit('badMessage', senderId);
|
var added = this.addSeenToTxProposals();
|
||||||
this.log('Received BAD TxProposal messsage FROM:', senderId); //TODO
|
|
||||||
return;
|
this.emit('txProposalsUpdated');
|
||||||
}
|
this.store();
|
||||||
|
|
||||||
var newId = ids[0];
|
|
||||||
var mergeInfo = this.txProposals.merge(inTxp, true);
|
|
||||||
var addSeen = this.addSeenToTxProposals();
|
|
||||||
if (mergeInfo.hasChanged || addSeen) {
|
|
||||||
this.log('### BROADCASTING txProposals. ');
|
|
||||||
recipients = null;
|
|
||||||
this.sendTxProposals(recipients, newId);
|
|
||||||
}
|
|
||||||
if (data.lastInBatch) {
|
|
||||||
this.emit('txProposalsUpdated');
|
|
||||||
this.store();
|
|
||||||
}
|
|
||||||
for (var i = 0; i < mergeInfo.events.length; i++) {
|
for (var i = 0; i < mergeInfo.events.length; i++) {
|
||||||
this.emit('txProposalEvent', mergeInfo.events[i]);
|
this.emit('txProposalEvent', mergeInfo.events[i]);
|
||||||
}
|
}
|
||||||
|
@ -156,13 +149,13 @@ Wallet.prototype._handleData = function(senderId, data, isInbound) {
|
||||||
break;
|
break;
|
||||||
case 'walletReady':
|
case 'walletReady':
|
||||||
this.sendPublicKeyRing(senderId);
|
this.sendPublicKeyRing(senderId);
|
||||||
this.sendTxProposals(senderId); // send old
|
this.sendAllTxProposals(senderId); // send old txps
|
||||||
break;
|
break;
|
||||||
case 'publicKeyRing':
|
case 'publicKeyRing':
|
||||||
this._handlePublicKeyRing(senderId, data, isInbound);
|
this._handlePublicKeyRing(senderId, data, isInbound);
|
||||||
break;
|
break;
|
||||||
case 'txProposals':
|
case 'txProposal':
|
||||||
this._handleTxProposals(senderId, data, isInbound);
|
this._handleTxProposal(senderId, data, isInbound);
|
||||||
break;
|
break;
|
||||||
case 'indexes':
|
case 'indexes':
|
||||||
this._handleIndexes(senderId, data, isInbound);
|
this._handleIndexes(senderId, data, isInbound);
|
||||||
|
@ -340,9 +333,9 @@ Wallet.prototype.toObj = function() {
|
||||||
|
|
||||||
Wallet.fromObj = function(o, storage, network, blockchain) {
|
Wallet.fromObj = function(o, storage, network, blockchain) {
|
||||||
var opts = JSON.parse(JSON.stringify(o.opts));
|
var opts = JSON.parse(JSON.stringify(o.opts));
|
||||||
opts.publicKeyRing = copay.PublicKeyRing.fromObj(o.publicKeyRing);
|
opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing);
|
||||||
opts.txProposals = copay.TxProposals.fromObj(o.txProposals);
|
opts.txProposals = TxProposals.fromObj(o.txProposals);
|
||||||
opts.privateKey = copay.PrivateKey.fromObj(o.privateKey);
|
opts.privateKey = PrivateKey.fromObj(o.privateKey);
|
||||||
|
|
||||||
opts.storage = storage;
|
opts.storage = storage;
|
||||||
opts.network = network;
|
opts.network = network;
|
||||||
|
@ -358,25 +351,25 @@ Wallet.prototype.toEncryptedObj = function() {
|
||||||
return this.storage.export(walletObj);
|
return this.storage.export(walletObj);
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.sendTxProposals = function(recipients, ntxid) {
|
Wallet.prototype.sendAllTxProposals = function(recipients) {
|
||||||
this.log('### SENDING txProposals TO:', recipients || 'All', this.txProposals);
|
var ntxids = this.txProposals.getNtxids();
|
||||||
|
for (var i in ntxids) {
|
||||||
var toSend = ntxid ? [ntxid] : this.txProposals.getNtxids();
|
var ntxid = ntxids[i];
|
||||||
|
this.sendTxProposal(ntxid, recipients);
|
||||||
var last = toSend[toSend];
|
|
||||||
|
|
||||||
for (var i in toSend) {
|
|
||||||
var id = toSend[i];
|
|
||||||
var lastInBatch = (i == toSend.length - 1);
|
|
||||||
this.network.send(recipients, {
|
|
||||||
type: 'txProposals',
|
|
||||||
txProposals: this.txProposals.toObj(id),
|
|
||||||
walletId: this.id,
|
|
||||||
lastInBatch: lastInBatch,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.sendTxProposal = function(ntxid, recipients) {
|
||||||
|
preconditions.checkArgument(ntxid);
|
||||||
|
preconditions.checkState(this.txProposals.txps[ntxid]);
|
||||||
|
this.log('### SENDING txProposal '+ntxid+' TO:', recipients || 'All', this.txProposals);
|
||||||
|
this.network.send(recipients, {
|
||||||
|
type: 'txProposal',
|
||||||
|
txProposal: this.txProposals.txps[ntxid].toObj(),
|
||||||
|
walletId: this.id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Wallet.prototype.sendWalletReady = function(recipients) {
|
Wallet.prototype.sendWalletReady = function(recipients) {
|
||||||
this.log('### SENDING WalletReady TO:', recipients);
|
this.log('### SENDING WalletReady TO:', recipients);
|
||||||
|
|
||||||
|
@ -440,14 +433,15 @@ Wallet.prototype.generateAddress = function(isChange, cb) {
|
||||||
Wallet.prototype.getTxProposals = function() {
|
Wallet.prototype.getTxProposals = function() {
|
||||||
var ret = [];
|
var ret = [];
|
||||||
var copayers = this.getRegisteredCopayerIds();
|
var copayers = this.getRegisteredCopayerIds();
|
||||||
for (var k in this.txProposals.txps) {
|
for (var ntxid in this.txProposals.txps) {
|
||||||
var i = this.txProposals.getTxProposal(k, copayers);
|
var txp = this.txProposals.getTxProposal(ntxid, copayers);
|
||||||
i.signedByUs = i.signedBy[this.getMyCopayerId()] ? true : false;
|
txp.signedByUs = txp.signedBy[this.getMyCopayerId()] ? true : false;
|
||||||
i.rejectedByUs = i.rejectedBy[this.getMyCopayerId()] ? true : false;
|
txp.rejectedByUs = txp.rejectedBy[this.getMyCopayerId()] ? true : false;
|
||||||
if (this.totalCopayers - i.rejectCount < this.requiredCopayers)
|
if (this.totalCopayers - txp.rejectCount < this.requiredCopayers) {
|
||||||
i.finallyRejected = true;
|
txp.finallyRejected = true;
|
||||||
|
}
|
||||||
|
|
||||||
ret.push(i);
|
ret.push(txp);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
@ -461,7 +455,7 @@ Wallet.prototype.reject = function(ntxid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
txp.rejectedBy[myId] = Date.now();
|
txp.rejectedBy[myId] = Date.now();
|
||||||
this.sendTxProposals(null, ntxid);
|
this.sendTxProposal(ntxid);
|
||||||
this.store();
|
this.store();
|
||||||
this.emit('txProposalsUpdated');
|
this.emit('txProposalsUpdated');
|
||||||
};
|
};
|
||||||
|
@ -486,7 +480,7 @@ Wallet.prototype.sign = function(ntxid, cb) {
|
||||||
var ret = false;
|
var ret = false;
|
||||||
if (b.signaturesAdded > before) {
|
if (b.signaturesAdded > before) {
|
||||||
txp.signedBy[myId] = Date.now();
|
txp.signedBy[myId] = Date.now();
|
||||||
self.sendTxProposals(null, ntxid);
|
self.sendTxProposal(ntxid);
|
||||||
self.store();
|
self.store();
|
||||||
self.emit('txProposalsUpdated');
|
self.emit('txProposalsUpdated');
|
||||||
ret = true;
|
ret = true;
|
||||||
|
@ -514,7 +508,7 @@ Wallet.prototype.sendTx = function(ntxid, cb) {
|
||||||
self.log('BITCOIND txid:', txid);
|
self.log('BITCOIND txid:', txid);
|
||||||
if (txid) {
|
if (txid) {
|
||||||
self.txProposals.setSent(ntxid, txid);
|
self.txProposals.setSent(ntxid, txid);
|
||||||
self.sendTxProposals(null, ntxid);
|
self.sendTxProposal(ntxid);
|
||||||
self.store();
|
self.store();
|
||||||
}
|
}
|
||||||
return cb(txid);
|
return cb(txid);
|
||||||
|
@ -641,7 +635,7 @@ Wallet.prototype.createTx = function(toAddress, amountSatStr, comment, opts, cb)
|
||||||
var ntxid = self.createTxSync(toAddress, amountSatStr, comment, safeUnspent, opts);
|
var ntxid = self.createTxSync(toAddress, amountSatStr, comment, safeUnspent, opts);
|
||||||
if (ntxid) {
|
if (ntxid) {
|
||||||
self.sendIndexes();
|
self.sendIndexes();
|
||||||
self.sendTxProposals(null, ntxid);
|
self.sendTxProposal(ntxid);
|
||||||
self.store();
|
self.store();
|
||||||
self.emit('txProposalsUpdated');
|
self.emit('txProposalsUpdated');
|
||||||
}
|
}
|
||||||
|
@ -711,6 +705,71 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
|
||||||
return ntxid;
|
return ntxid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.updateIndexes = function(callback) {
|
||||||
|
var self = this;
|
||||||
|
var start = self.publicKeyRing.indexes.changeIndex;
|
||||||
|
self.indexDiscovery(start, true, 20, function(err, changeIndex) {
|
||||||
|
if (err) return callback(err);
|
||||||
|
if (changeIndex != -1)
|
||||||
|
self.publicKeyRing.indexes.changeIndex = changeIndex + 1;
|
||||||
|
|
||||||
|
start = self.publicKeyRing.indexes.receiveIndex;
|
||||||
|
self.indexDiscovery(start, false, 20, function(err, receiveIndex) {
|
||||||
|
if (err) return callback(err);
|
||||||
|
if (receiveIndex != -1)
|
||||||
|
self.publicKeyRing.indexes.receiveIndex = receiveIndex + 1;
|
||||||
|
|
||||||
|
self.emit('publicKeyRingUpdated');
|
||||||
|
self.store();
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Wallet.prototype.deriveAddresses = function(index, amout, isChange) {
|
||||||
|
var ret = new Array(amout);
|
||||||
|
for(var i = 0; i < amout; i++) {
|
||||||
|
ret[i] = this.publicKeyRing.getAddress(index + i, isChange).toString();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function scans the publicKeyRing branch starting at index @start and reports the index with last activity,
|
||||||
|
// using a scan window of @gap. The argument @change defines the branch to scan: internal or external.
|
||||||
|
// Returns -1 if no activity is found in range.
|
||||||
|
Wallet.prototype.indexDiscovery = function(start, change, gap, cb) {
|
||||||
|
var scanIndex = start;
|
||||||
|
var lastActive = -1;
|
||||||
|
var hasActivity = false;
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
async.doWhilst(
|
||||||
|
function _do(next) {
|
||||||
|
// Optimize window to minimize the derivations.
|
||||||
|
var scanWindow = (lastActive == -1) ? gap : gap - (scanIndex - lastActive) + 1;
|
||||||
|
var addresses = self.deriveAddresses(scanIndex, scanWindow, change);
|
||||||
|
self.blockchain.checkActivity(addresses, function(err, actives){
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
// Check for new activities in the newlly scanned addresses
|
||||||
|
var recentActive = actives.reduce(function(r, e, i) {
|
||||||
|
return e ? scanIndex + i : r;
|
||||||
|
}, lastActive);
|
||||||
|
hasActivity = lastActive != recentActive;
|
||||||
|
lastActive = recentActive;
|
||||||
|
scanIndex += scanWindow;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function _while() { return hasActivity; },
|
||||||
|
function _finnaly(err) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
cb(null, lastActive);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Wallet.prototype.disconnect = function() {
|
Wallet.prototype.disconnect = function() {
|
||||||
this.log('## DISCONNECTING');
|
this.log('## DISCONNECTING');
|
||||||
this.network.disconnect();
|
this.network.disconnect();
|
||||||
|
|
|
@ -74,6 +74,16 @@ WalletFactory.prototype.fromEncryptedObj = function(base64, password) {
|
||||||
return w;
|
return w;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
WalletFactory.prototype.import = function(base64, password, cb) {
|
||||||
|
var self = this;
|
||||||
|
var w = self.fromEncryptedObj(base64, password);
|
||||||
|
w.updateIndexes(function(err) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
self.log('Indexes updated');
|
||||||
|
cb(null, w);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
WalletFactory.prototype.read = function(walletId) {
|
WalletFactory.prototype.read = function(walletId) {
|
||||||
if (!this._checkRead(walletId))
|
if (!this._checkRead(walletId))
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var BackupService = function($notification) {
|
|
||||||
this.notifications = $notification;
|
var BackupService = function(notification) {
|
||||||
|
this.notifications = notification;
|
||||||
};
|
};
|
||||||
|
|
||||||
BackupService.prototype.getName = function(wallet) {
|
BackupService.prototype.getName = function(wallet) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
|
|
||||||
angular.module('copayApp.services')
|
angular.module('copayApp.services')
|
||||||
.factory('controllerUtils', function($rootScope, $sce, $location, $notification, $timeout, Socket, video) {
|
.factory('controllerUtils', function($rootScope, $sce, $location, notification, $timeout, Socket, video) {
|
||||||
var root = {};
|
var root = {};
|
||||||
|
|
||||||
root.getVideoMutedStatus = function(copayer) {
|
root.getVideoMutedStatus = function(copayer) {
|
||||||
|
@ -87,7 +87,7 @@ angular.module('copayApp.services')
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
};
|
};
|
||||||
|
|
||||||
$notification.enableHtml5Mode(); // for chrome: if support, enable it
|
notification.enableHtml5Mode(); // for chrome: if support, enable it
|
||||||
|
|
||||||
w.on('badMessage', function(peerId) {
|
w.on('badMessage', function(peerId) {
|
||||||
$rootScope.$flashMessage = {
|
$rootScope.$flashMessage = {
|
||||||
|
@ -126,11 +126,11 @@ angular.module('copayApp.services')
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
case 'signed':
|
case 'signed':
|
||||||
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
||||||
$notification.info('Transaction Update', 'A transaction was signed by ' + user);
|
notification.info('Transaction Update', 'A transaction was signed by ' + user);
|
||||||
break;
|
break;
|
||||||
case 'rejected':
|
case 'rejected':
|
||||||
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
||||||
$notification.info('Transaction Update', 'A transaction was rejected by ' + user);
|
notification.info('Transaction Update', 'A transaction was rejected by ' + user);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.services').
|
angular.module('copayApp.services').
|
||||||
factory('$notification', ['$timeout',function($timeout){
|
factory('notification', ['$timeout',function($timeout){
|
||||||
|
|
||||||
var notifications = JSON.parse(localStorage.getItem('$notifications')) || [],
|
var notifications = JSON.parse(localStorage.getItem('notifications')) || [],
|
||||||
queue = [];
|
queue = [];
|
||||||
|
|
||||||
var settings = {
|
var settings = {
|
||||||
|
@ -186,7 +186,7 @@ angular.module('copayApp.services').
|
||||||
save: function(){
|
save: function(){
|
||||||
// Save all the notifications into localStorage
|
// Save all the notifications into localStorage
|
||||||
if(settings.localStorage){
|
if(settings.localStorage){
|
||||||
localStorage.setItem('$notifications', JSON.stringify(notifications));
|
localStorage.setItem('notifications', JSON.stringify(notifications));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ angular.module('copayApp.services').
|
||||||
|
|
||||||
};
|
};
|
||||||
}]).
|
}]).
|
||||||
directive('notifications', ['$notification', '$compile', function($notification, $compile){
|
directive('notifications', function(notification, $compile){
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* It should also parse the arguments passed to it that specify
|
* It should also parse the arguments passed to it that specify
|
||||||
|
@ -245,7 +245,7 @@ angular.module('copayApp.services').
|
||||||
template: html,
|
template: html,
|
||||||
link: link,
|
link: link,
|
||||||
controller: ['$scope', function NotificationsCtrl( $scope ){
|
controller: ['$scope', function NotificationsCtrl( $scope ){
|
||||||
$scope.queue = $notification.getQueue();
|
$scope.queue = notification.getQueue();
|
||||||
|
|
||||||
$scope.removeNotification = function(noti){
|
$scope.removeNotification = function(noti){
|
||||||
$scope.queue.splice($scope.queue.indexOf(noti), 1);
|
$scope.queue.splice($scope.queue.indexOf(noti), 1);
|
||||||
|
@ -254,4 +254,9 @@ angular.module('copayApp.services').
|
||||||
]
|
]
|
||||||
|
|
||||||
};
|
};
|
||||||
}]);
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,9 @@ var Video = function() {
|
||||||
|
|
||||||
this.mediaConnections = {};
|
this.mediaConnections = {};
|
||||||
this.localStream = null;
|
this.localStream = null;
|
||||||
this.onlineSound = new Audio('sound/online.wav');
|
if (typeof Audio !== 'undefined') {
|
||||||
|
this.onlineSound = new Audio('sound/online.wav');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Video.prototype.setOwnPeer = function(peer, wallet, cb) {
|
Video.prototype.setOwnPeer = function(peer, wallet, cb) {
|
||||||
|
@ -64,7 +66,7 @@ Video.prototype._addCall = function(mediaConnection, cb) {
|
||||||
|
|
||||||
// Wait for stream on the call, then set peer video display
|
// Wait for stream on the call, then set peer video display
|
||||||
mediaConnection.on('stream', function(stream) {
|
mediaConnection.on('stream', function(stream) {
|
||||||
self.onlineSound.play();
|
if (self.onlineSound) self.onlineSound.play();
|
||||||
cb(null, peerID, URL.createObjectURL(stream));
|
cb(null, peerID, URL.createObjectURL(stream));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -39,15 +39,6 @@ module.exports = function(config) {
|
||||||
'lib/qrcode-decoder-js/lib/qrcode-decoder.min.js',
|
'lib/qrcode-decoder-js/lib/qrcode-decoder.min.js',
|
||||||
'js/copayBundle.js',
|
'js/copayBundle.js',
|
||||||
|
|
||||||
//Test-Specific Code
|
|
||||||
'lib/chai/chai.js',
|
|
||||||
'test/lib/chai-should.js',
|
|
||||||
'test/lib/chai-expect.js',
|
|
||||||
'test/mocks/FakeWallet.js',
|
|
||||||
|
|
||||||
//Mocha stuff
|
|
||||||
'test/mocha.conf.js',
|
|
||||||
|
|
||||||
//App-specific Code
|
//App-specific Code
|
||||||
'js/app.js',
|
'js/app.js',
|
||||||
'js/routes.js',
|
'js/routes.js',
|
||||||
|
@ -57,6 +48,13 @@ module.exports = function(config) {
|
||||||
'js/controllers/*.js',
|
'js/controllers/*.js',
|
||||||
'js/init.js',
|
'js/init.js',
|
||||||
|
|
||||||
|
//Test-Specific Code
|
||||||
|
'lib/chai/chai.js',
|
||||||
|
'test/lib/chai-should.js',
|
||||||
|
'test/lib/chai-expect.js',
|
||||||
|
'test/mocks/FakeWallet.js',
|
||||||
|
|
||||||
|
'test/mocha.conf.js',
|
||||||
|
|
||||||
//test files
|
//test files
|
||||||
'test/unit/**/*.js'
|
'test/unit/**/*.js'
|
||||||
|
|
10
package.json
10
package.json
|
@ -34,9 +34,11 @@
|
||||||
"browserify": "3.32.1",
|
"browserify": "3.32.1",
|
||||||
"buffertools": "2.0.1",
|
"buffertools": "2.0.1",
|
||||||
"chai": "1.9.1",
|
"chai": "1.9.1",
|
||||||
|
"cli-color": "0.3.2",
|
||||||
"commander": "2.1.0",
|
"commander": "2.1.0",
|
||||||
"coveralls": "2.10.0",
|
"coveralls": "2.10.0",
|
||||||
"express": "4.0.0",
|
"express": "4.0.0",
|
||||||
|
"github-releases": "0.2.0",
|
||||||
"grunt-browserify": "2.0.8",
|
"grunt-browserify": "2.0.8",
|
||||||
"grunt-contrib-watch": "0.5.3",
|
"grunt-contrib-watch": "0.5.3",
|
||||||
"grunt-markdown": "0.5.0",
|
"grunt-markdown": "0.5.0",
|
||||||
|
@ -46,18 +48,18 @@
|
||||||
"karma": "0.12.9",
|
"karma": "0.12.9",
|
||||||
"karma-chrome-launcher": "0.1.3",
|
"karma-chrome-launcher": "0.1.3",
|
||||||
"karma-mocha": "0.1.3",
|
"karma-mocha": "0.1.3",
|
||||||
|
"karma-phantomjs-launcher": "^0.1.4",
|
||||||
"mocha": "1.18.2",
|
"mocha": "1.18.2",
|
||||||
"mocha-lcov-reporter": "0.0.1",
|
"mocha-lcov-reporter": "0.0.1",
|
||||||
"node-cryptojs-aes": "0.4.0",
|
"node-cryptojs-aes": "0.4.0",
|
||||||
"sinon": "1.9.1",
|
"sinon": "1.9.1",
|
||||||
"soop": "0.1.5",
|
"soop": "0.1.5",
|
||||||
"travis-cov": "0.2.5",
|
"travis-cov": "0.2.5",
|
||||||
"uglifyify": "1.2.3",
|
"uglifyify": "1.2.3"
|
||||||
"github-releases": "0.2.0",
|
|
||||||
"cli-color": "0.3.2"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mocha": "^1.18.2",
|
"mocha": "^1.18.2",
|
||||||
"mocha-lcov-reporter": "0.0.1"
|
"mocha-lcov-reporter": "0.0.1",
|
||||||
|
"preconditions": "^1.0.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,8 +97,8 @@ describe('TxProposals model', function() {
|
||||||
priv,
|
priv,
|
||||||
pkr
|
pkr
|
||||||
));
|
));
|
||||||
var k = Object.keys(w.txps)[0];
|
var ntxid = Object.keys(w.txps)[0];
|
||||||
var b = w.txps[k].builder;
|
var b = w.txps[ntxid].builder;
|
||||||
var tx = b.build();
|
var tx = b.build();
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
b.sign(priv2.getAll(pkr.indexes.getReceiveIndex(), pkr.indexes.getChangeIndex()));
|
b.sign(priv2.getAll(pkr.indexes.getReceiveIndex(), pkr.indexes.getChangeIndex()));
|
||||||
|
@ -211,15 +211,15 @@ describe('TxProposals model', function() {
|
||||||
priv,
|
priv,
|
||||||
pkr
|
pkr
|
||||||
));
|
));
|
||||||
var k = Object.keys(w.txps)[0];
|
var ntxid = Object.keys(w.txps)[0];
|
||||||
var tx = w.txps[k].builder.build();
|
var tx = w.txps[ntxid].builder.build();
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
tx.countInputMissingSignatures(0).should.equal(2);
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
(w.txps[k].signedBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
(w.txps[k].seenBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
var info = w.merge(w);
|
var info = w.merge(w.txps[ntxid]);
|
||||||
info.events.length.should.equal(0);
|
info.events.length.should.equal(0);
|
||||||
|
|
||||||
Object.keys(w.txps).length.should.equal(1);
|
Object.keys(w.txps).length.should.equal(1);
|
||||||
|
@ -227,8 +227,8 @@ describe('TxProposals model', function() {
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
tx.countInputMissingSignatures(0).should.equal(2);
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
(w.txps[k].signedBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
(w.txps[k].seenBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -261,13 +261,13 @@ describe('TxProposals model', function() {
|
||||||
pkr
|
pkr
|
||||||
));
|
));
|
||||||
|
|
||||||
var k = Object.keys(w.txps)[0];
|
var ntxid = Object.keys(w.txps)[0];
|
||||||
var tx = w.txps[k].builder.build();
|
var tx = w.txps[ntxid].builder.build();
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
tx.countInputMissingSignatures(0).should.equal(1);
|
tx.countInputMissingSignatures(0).should.equal(1);
|
||||||
|
|
||||||
Object.keys(w.txps[k].signedBy).length.should.equal(0);
|
Object.keys(w.txps[ntxid].signedBy).length.should.equal(0);
|
||||||
Object.keys(w.txps[k].seenBy).length.should.equal(1);
|
Object.keys(w.txps[ntxid].seenBy).length.should.equal(1);
|
||||||
|
|
||||||
|
|
||||||
var w2 = new TxProposals({
|
var w2 = new TxProposals({
|
||||||
|
@ -285,25 +285,25 @@ describe('TxProposals model', function() {
|
||||||
pkr
|
pkr
|
||||||
));
|
));
|
||||||
|
|
||||||
var k = Object.keys(w.txps)[0];
|
var ntxid = Object.keys(w.txps)[0];
|
||||||
var tx = w2.txps[k].builder.build();
|
var tx = w2.txps[ntxid].builder.build();
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
tx.countInputMissingSignatures(0).should.equal(2);
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
(w2.txps[k].signedBy[priv.id] - ts > 0).should.equal(true);
|
(w2.txps[ntxid].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
(w2.txps[k].seenBy[priv.id] - ts > 0).should.equal(true);
|
(w2.txps[ntxid].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
var info = w.merge(w2);
|
var info = w.merge(w2.txps[ntxid]);
|
||||||
info.events.length.should.equal(1);
|
info.events.length.should.equal(1);
|
||||||
info.events[0].type.should.equal('signed');
|
info.events[0].type.should.equal('signed');
|
||||||
|
|
||||||
Object.keys(w.txps).length.should.equal(1);
|
Object.keys(w.txps).length.should.equal(1);
|
||||||
|
|
||||||
var tx = w.txps[k].builder.build();
|
var tx = w.txps[ntxid].builder.build();
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
tx.countInputMissingSignatures(0).should.equal(2);
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
(w.txps[k].signedBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
(w.txps[k].seenBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -401,7 +401,7 @@ describe('TxProposals model', function() {
|
||||||
(w2.txps[ntxid].signedBy[priv.id] - ts > 0).should.equal(true);
|
(w2.txps[ntxid].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
(w2.txps[ntxid].seenBy[priv.id] - ts > 0).should.equal(true);
|
(w2.txps[ntxid].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
var info = w.merge(w2);
|
var info = w.merge(w2.txps[ntxid]);
|
||||||
info.events.length.should.equal(1);
|
info.events.length.should.equal(1);
|
||||||
info.events[0].type.should.equal('signed');
|
info.events[0].type.should.equal('signed');
|
||||||
|
|
||||||
|
@ -431,7 +431,7 @@ describe('TxProposals model', function() {
|
||||||
(w3.txps[ntxid].signedBy[priv2.id] - ts > 0).should.equal(true);
|
(w3.txps[ntxid].signedBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
(w3.txps[ntxid].seenBy[priv2.id] - ts > 0).should.equal(true);
|
(w3.txps[ntxid].seenBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
var info = w.merge(w3);
|
var info = w.merge(w3.txps[ntxid]);
|
||||||
info.events.length.should.equal(0);
|
info.events.length.should.equal(0);
|
||||||
|
|
||||||
Object.keys(w.txps).length.should.equal(1);
|
Object.keys(w.txps).length.should.equal(1);
|
||||||
|
@ -475,12 +475,12 @@ describe('TxProposals model', function() {
|
||||||
priv,
|
priv,
|
||||||
pkr
|
pkr
|
||||||
));
|
));
|
||||||
var k = Object.keys(w.txps)[0];
|
var ntxid = Object.keys(w.txps)[0];
|
||||||
var tx = w.txps[k].builder.build();
|
var tx = w.txps[ntxid].builder.build();
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
tx.countInputMissingSignatures(0).should.equal(2);
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
(w.txps[k].signedBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
(w.txps[k].seenBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
|
||||||
var w2 = new TxProposals({
|
var w2 = new TxProposals({
|
||||||
|
@ -496,11 +496,11 @@ describe('TxProposals model', function() {
|
||||||
priv2,
|
priv2,
|
||||||
pkr
|
pkr
|
||||||
));
|
));
|
||||||
var tx = w2.txps[k].builder.build();
|
var tx = w2.txps[ntxid].builder.build();
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
tx.countInputMissingSignatures(0).should.equal(2);
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
(w2.txps[k].signedBy[priv2.id] - ts > 0).should.equal(true);
|
(w2.txps[ntxid].signedBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
(w2.txps[k].seenBy[priv2.id] - ts > 0).should.equal(true);
|
(w2.txps[ntxid].seenBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
|
||||||
var w3 = new TxProposals({
|
var w3 = new TxProposals({
|
||||||
|
@ -516,38 +516,38 @@ describe('TxProposals model', function() {
|
||||||
priv3,
|
priv3,
|
||||||
pkr
|
pkr
|
||||||
));
|
));
|
||||||
var tx = w3.txps[k].builder.build();
|
var tx = w3.txps[ntxid].builder.build();
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
tx.countInputMissingSignatures(0).should.equal(2);
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
(w3.txps[k].signedBy[priv3.id] - ts > 0).should.equal(true);
|
(w3.txps[ntxid].signedBy[priv3.id] - ts > 0).should.equal(true);
|
||||||
(w3.txps[k].seenBy[priv3.id] - ts > 0).should.equal(true);
|
(w3.txps[ntxid].seenBy[priv3.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
var info = w.merge(w2);
|
var info = w.merge(w2.txps[ntxid]);
|
||||||
info.events.length.should.equal(0);
|
info.events.length.should.equal(0);
|
||||||
|
|
||||||
Object.keys(w.txps).length.should.equal(1);
|
Object.keys(w.txps).length.should.equal(1);
|
||||||
var tx = w.txps[k].builder.build();
|
var tx = w.txps[ntxid].builder.build();
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
tx.countInputMissingSignatures(0).should.equal(1);
|
tx.countInputMissingSignatures(0).should.equal(1);
|
||||||
(w.txps[k].seenBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
(w.txps[k].seenBy[priv2.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].seenBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
(w.txps[k].signedBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
(w.txps[k].signedBy[priv2.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].signedBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
|
||||||
var info = w.merge(w3);
|
var info = w.merge(w3.txps[ntxid]);
|
||||||
info.events.length.should.equal(0);
|
info.events.length.should.equal(0);
|
||||||
|
|
||||||
var tx = w.txps[k].builder.build();
|
var tx = w.txps[ntxid].builder.build();
|
||||||
tx.isComplete().should.equal(true);
|
tx.isComplete().should.equal(true);
|
||||||
tx.countInputMissingSignatures(0).should.equal(0);
|
tx.countInputMissingSignatures(0).should.equal(0);
|
||||||
Object.keys(w.txps).length.should.equal(1);
|
Object.keys(w.txps).length.should.equal(1);
|
||||||
(w.txps[k].seenBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
(w.txps[k].seenBy[priv2.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].seenBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
(w.txps[k].seenBy[priv3.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].seenBy[priv3.id] - ts > 0).should.equal(true);
|
||||||
(w.txps[k].signedBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
(w.txps[k].signedBy[priv2.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].signedBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
(w.txps[k].signedBy[priv3.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].signedBy[priv3.id] - ts > 0).should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -573,12 +573,12 @@ describe('TxProposals model', function() {
|
||||||
priv,
|
priv,
|
||||||
pkr
|
pkr
|
||||||
));
|
));
|
||||||
var k = Object.keys(w.txps)[0];
|
var ntxid = Object.keys(w.txps)[0];
|
||||||
var tx = w.txps[k].builder.build();
|
var tx = w.txps[ntxid].builder.build();
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
tx.countInputMissingSignatures(0).should.equal(2);
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
(w.txps[k].signedBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
(w.txps[k].seenBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[ntxid].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
var o = w.toObj();
|
var o = w.toObj();
|
||||||
should.exist(o);
|
should.exist(o);
|
||||||
|
@ -593,15 +593,15 @@ describe('TxProposals model', function() {
|
||||||
var o2 = JSON.parse(JSON.stringify(o));
|
var o2 = JSON.parse(JSON.stringify(o));
|
||||||
var w2 = TxProposals.fromObj(o2);
|
var w2 = TxProposals.fromObj(o2);
|
||||||
w2.walletId.should.equal(w.walletId);
|
w2.walletId.should.equal(w.walletId);
|
||||||
var tx2 = w2.txps[k].builder.build();
|
var tx2 = w2.txps[ntxid].builder.build();
|
||||||
tx2.isComplete().should.equal(false);
|
tx2.isComplete().should.equal(false);
|
||||||
tx2.countInputMissingSignatures(0).should.equal(2);
|
tx2.countInputMissingSignatures(0).should.equal(2);
|
||||||
(w2.txps[k].signedBy[priv.id] - ts > 0).should.equal(true);
|
(w2.txps[ntxid].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
(w2.txps[k].seenBy[priv.id] - ts > 0).should.equal(true);
|
(w2.txps[ntxid].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
should.exist(w2.txps[k].builder);
|
should.exist(w2.txps[ntxid].builder);
|
||||||
should.exist(w2.txps[k].builder.valueInSat);
|
should.exist(w2.txps[ntxid].builder.valueInSat);
|
||||||
|
|
||||||
w2.merge(w);
|
w2.merge(w.txps[ntxid]);
|
||||||
Object.keys(w2.txps).length.should.equal(1);
|
Object.keys(w2.txps).length.should.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,10 @@ describe('Wallet model', function() {
|
||||||
}).should.
|
}).should.
|
||||||
throw();
|
throw();
|
||||||
});
|
});
|
||||||
|
it('should getNetworkName', function() {
|
||||||
|
var w = createW();
|
||||||
|
w.getNetworkName().should.equal('testnet');
|
||||||
|
});
|
||||||
|
|
||||||
var createW = function(netKey, N) {
|
var createW = function(netKey, N) {
|
||||||
|
|
||||||
|
@ -293,23 +297,13 @@ describe('Wallet model', function() {
|
||||||
it('call reconnect after interval', function(done) {
|
it('call reconnect after interval', function(done) {
|
||||||
this.timeout(10000);
|
this.timeout(10000);
|
||||||
var w = createW2();
|
var w = createW2();
|
||||||
var testTime = 1000;
|
|
||||||
var callCount = 0;
|
|
||||||
var cT = w.reconnectDelay;
|
|
||||||
var t = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
callCount++;
|
|
||||||
t += cT;
|
|
||||||
cT *= 2;
|
|
||||||
} while (t < testTime);
|
|
||||||
|
|
||||||
var spy = sinon.spy(w, 'scheduleConnect');
|
var spy = sinon.spy(w, 'scheduleConnect');
|
||||||
|
var callCount = 3;
|
||||||
w.netStart();
|
w.netStart();
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
sinon.assert.callCount(spy, callCount);
|
sinon.assert.callCount(spy, callCount);
|
||||||
done();
|
done();
|
||||||
}, testTime);
|
}, w.reconnectDelay*callCount*(callCount+1)/2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handle network indexes correctly', function() {
|
it('handle network indexes correctly', function() {
|
||||||
|
@ -357,52 +351,49 @@ describe('Wallet model', function() {
|
||||||
|
|
||||||
it('handle network txProposals correctly', function() {
|
it('handle network txProposals correctly', function() {
|
||||||
var w = createW();
|
var w = createW();
|
||||||
var txps = {
|
var txp = {
|
||||||
'txProposals': {
|
'txProposal': {
|
||||||
"txps": [{
|
"seenBy": {
|
||||||
"seenBy": {
|
"undefined": 1402337282806
|
||||||
"undefined": 1402337282806
|
},
|
||||||
|
"signedBy": {
|
||||||
|
"undefined": 1402337282841
|
||||||
|
},
|
||||||
|
"rejectedBy": {},
|
||||||
|
"sentTs": null,
|
||||||
|
"sentTxid": null,
|
||||||
|
"inputChainPaths": [],
|
||||||
|
"builderObj": {
|
||||||
|
"valueInSat": "1000000000",
|
||||||
|
"valueOutSat": "123456789",
|
||||||
|
"feeSat": "10000",
|
||||||
|
"remainderSat": "876533211",
|
||||||
|
"hashToScriptMap": {
|
||||||
|
"3QjgeBsNeiDkoVQxDAw4sSEe9BVaqhmpnd": "5321025c2951a7e94e39f2c3210fb2d71b7d79044f7daa6b007556a6feb4b2059091e921026c56eea4ef632b24c4efe9d5391ea0e15e306450e5b7db26cd1a7a33d9d4e82821026d06e532385186793121633fb365fa2f0c7246b30590db78cba795641c94734521035051d2b742263bc5257c5a4461e1a6fa32c3f1965f5ab5e8b0f0f80c9e28bc702103ddb0d39c1cd88295e7b81050dcab11ca4419bf64fba3ef9d9130ea64d6604ab055ae",
|
||||||
|
"365dPtmHPAdUUNvPBcnfidYt2Snyaka7AH": "5321020faaf739abda51e822621d42062470c5743a127186369a67fd3303f71727fb1d210237063d3b27c21767054dad203298bfed90cb08dca371950a08b88aa979c33bd22102a3780cb275b6dcb8b379514679387d4578068745f08c5799e4ee1a90aa3a70922102f4fea0cd5bc2418892278f480682690857f4d963deeb95d88febfa255e4ce0b821035d53fa74fee26873c3f5a4acf4dce53ff9d50db59e1ca4acddb82168dd429a3355ae",
|
||||||
|
"372DueknmmjVR3zcWfEfADp8FwJ3ARhGaN": "532102666c2873c9e6f58ca594cd6c8327a1515db32ebadbaff4fe75e63b917560b7d021031ca2d159ae8868a5eef6d67a5a8a5bcc6fb2e8b711669f1c1e8b05327236a3cf2103798a4ce34929cb450bf88557dd90cae538c67f9d0c76314ae18679200be9c17021039624dec23dc1bb628cea55e949ea26225949a3349346a0732fef9def6f1f75292103d1edaadb8555012b752dd7a5c6672c379ce827e4bc0d4e992ee1ab3488b445b255ae",
|
||||||
|
"3GLJbHv7RqPeFk2SqWapqJ3XibVibiKi8f": "5321021a33d48b9f5d3adc41004709313277d4c6969268cf41e3f5b695a934c676829a21031b87321307db7a0f6ea22847a538505188f1a2231eba68867e17c5f0e4434c0721035dee0a6e1df66a6c9c7592ef03aa02bba9b06742134d27bd08f356e33f21259c2103768a109d682a76c09f865912e9e64a8689b03c1a231c613d87ec9bd535fd74c22103911038638b9fc52b2d375ce207c8388bd5ee7f2d54ab9b4148bd406c6d7dcad355ae",
|
||||||
|
"3E1d1z7gJSFeZM2d3k12MJ1jGwrBRy1YTw": "5321027da98ce0407138461f4ad803a1fb67afa4246f06ad0e1256a087daeffd82a8642102e7f87f2b094ec322be2fb99d3ca81fd6ac0ab26ce081e51ab18c507f8b6d1d162102ed5d256036e10568c33e0a15021cc1806e04d7d24c8f020aaae23ec2deecb4302103b6f88231bb5a5f4629e4d097492321f86968c6aeb012196a1fe3b217fe4ae0ac2103f6b5e2c1db3124a5058a0e3e83d5d0c412b9a5b50e2ef97e2b7e1c0af57ab3e355ae",
|
||||||
|
"3QYueyPXq2QDRQCDo7agTimz9673NKV25E": "5321021543292c2942708ccc83354ebf507044b310ed0d33a19e2327a49be53e0f314221024a1a83f8c50f6cad7c134b9cded509dabf196ae49eca157a39ad95798943dc95210292698fbb97e8f6e67296b22b36367ba85c8101fcbc27bb4b00b43623639212ac2102d86980796027a00ba7aa9b53671762c908962654f969f5dec340071bb30e8d7621038fb3fa5e77dafd24c09d819dbdc7c11dca55b350511bf3bc499c09316a89286e55ae",
|
||||||
|
"372BzC1GGjziT8zGYbryja3kF2KaTeobRK": "53210214ec25e5cb42e51883d4e615316748feefe91133fcfc0f93f0b5a24a55e0a347210262336210b3173aa4ca90d292990f463e42bdeb2e73112925dc712c5a2e749bcb210277517855f512564f225e63c650dad7720565aa563901d50743be4b0f0267dcc72102d2777a9faf5d2e2b2363e1270d61021bc2e36e8cb19ca9d29dedbba9a0348b532103a57db80b6ae573e5cef2115e36e73e5ef41f8a099bfb5087d80320c04a7db72e55ae"
|
||||||
},
|
},
|
||||||
"signedBy": {
|
"selectedUtxos": [{
|
||||||
"undefined": 1402337282841
|
"address": "3E1d1z7gJSFeZM2d3k12MJ1jGwrBRy1YTw",
|
||||||
},
|
"scriptPubKey": "a91487264aa41e3df76f3156c7fa587fd7d5b1f7b96b87",
|
||||||
"rejectedBy": {},
|
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
|
||||||
"sentTs": null,
|
"vout": 1,
|
||||||
"sentTxid": null,
|
"amount": 10,
|
||||||
"inputChainPaths": [],
|
"confirmations": 7
|
||||||
"builderObj": {
|
}],
|
||||||
"valueInSat": "1000000000",
|
"inputsSigned": 0,
|
||||||
"valueOutSat": "123456789",
|
"signaturesAdded": 1,
|
||||||
"feeSat": "10000",
|
"signhash": 1,
|
||||||
"remainderSat": "876533211",
|
"spendUnconfirmed": false,
|
||||||
"hashToScriptMap": {
|
"tx": "0100000001c1cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a01000000fd40010047304402201aad0ea92f929be21d60afb741b76bfcf2aa9614079cb9da76b15b3a1210f07b02205bc5c1895105da3ee682e532d2d49dfd1214fa6123fb9c9d552336f135c77eff0147304402203f02f15bb4ad4bc7d0ca4612fd9e9ae05a1331f3d62f302cf08dba56695f5dcf0220700110562fe228ebba324b120de64a7bbaddf049d67a313f749e9b32ee88787d014cad5321027da98ce0407138461f4ad803a1fb67afa4246f06ad0e1256a087daeffd82a8642102e7f87f2b094ec322be2fb99d3ca81fd6ac0ab26ce081e51ab18c507f8b6d1d162102ed5d256036e10568c33e0a15021cc1806e04d7d24c8f020aaae23ec2deecb4302103b6f88231bb5a5f4629e4d097492321f86968c6aeb012196a1fe3b217fe4ae0ac2103f6b5e2c1db3124a5058a0e3e83d5d0c412b9a5b50e2ef97e2b7e1c0af57ab3e355aeffffffff0215cd5b07000000001976a91434f8e0c5be216025a52addf18a987543cad23f7a88acdbd53e340000000017a914a09f10bc42b61ecf9d3d09550765c228f1cb808a8700000000"
|
||||||
"3QjgeBsNeiDkoVQxDAw4sSEe9BVaqhmpnd": "5321025c2951a7e94e39f2c3210fb2d71b7d79044f7daa6b007556a6feb4b2059091e921026c56eea4ef632b24c4efe9d5391ea0e15e306450e5b7db26cd1a7a33d9d4e82821026d06e532385186793121633fb365fa2f0c7246b30590db78cba795641c94734521035051d2b742263bc5257c5a4461e1a6fa32c3f1965f5ab5e8b0f0f80c9e28bc702103ddb0d39c1cd88295e7b81050dcab11ca4419bf64fba3ef9d9130ea64d6604ab055ae",
|
}
|
||||||
"365dPtmHPAdUUNvPBcnfidYt2Snyaka7AH": "5321020faaf739abda51e822621d42062470c5743a127186369a67fd3303f71727fb1d210237063d3b27c21767054dad203298bfed90cb08dca371950a08b88aa979c33bd22102a3780cb275b6dcb8b379514679387d4578068745f08c5799e4ee1a90aa3a70922102f4fea0cd5bc2418892278f480682690857f4d963deeb95d88febfa255e4ce0b821035d53fa74fee26873c3f5a4acf4dce53ff9d50db59e1ca4acddb82168dd429a3355ae",
|
|
||||||
"372DueknmmjVR3zcWfEfADp8FwJ3ARhGaN": "532102666c2873c9e6f58ca594cd6c8327a1515db32ebadbaff4fe75e63b917560b7d021031ca2d159ae8868a5eef6d67a5a8a5bcc6fb2e8b711669f1c1e8b05327236a3cf2103798a4ce34929cb450bf88557dd90cae538c67f9d0c76314ae18679200be9c17021039624dec23dc1bb628cea55e949ea26225949a3349346a0732fef9def6f1f75292103d1edaadb8555012b752dd7a5c6672c379ce827e4bc0d4e992ee1ab3488b445b255ae",
|
|
||||||
"3GLJbHv7RqPeFk2SqWapqJ3XibVibiKi8f": "5321021a33d48b9f5d3adc41004709313277d4c6969268cf41e3f5b695a934c676829a21031b87321307db7a0f6ea22847a538505188f1a2231eba68867e17c5f0e4434c0721035dee0a6e1df66a6c9c7592ef03aa02bba9b06742134d27bd08f356e33f21259c2103768a109d682a76c09f865912e9e64a8689b03c1a231c613d87ec9bd535fd74c22103911038638b9fc52b2d375ce207c8388bd5ee7f2d54ab9b4148bd406c6d7dcad355ae",
|
|
||||||
"3E1d1z7gJSFeZM2d3k12MJ1jGwrBRy1YTw": "5321027da98ce0407138461f4ad803a1fb67afa4246f06ad0e1256a087daeffd82a8642102e7f87f2b094ec322be2fb99d3ca81fd6ac0ab26ce081e51ab18c507f8b6d1d162102ed5d256036e10568c33e0a15021cc1806e04d7d24c8f020aaae23ec2deecb4302103b6f88231bb5a5f4629e4d097492321f86968c6aeb012196a1fe3b217fe4ae0ac2103f6b5e2c1db3124a5058a0e3e83d5d0c412b9a5b50e2ef97e2b7e1c0af57ab3e355ae",
|
|
||||||
"3QYueyPXq2QDRQCDo7agTimz9673NKV25E": "5321021543292c2942708ccc83354ebf507044b310ed0d33a19e2327a49be53e0f314221024a1a83f8c50f6cad7c134b9cded509dabf196ae49eca157a39ad95798943dc95210292698fbb97e8f6e67296b22b36367ba85c8101fcbc27bb4b00b43623639212ac2102d86980796027a00ba7aa9b53671762c908962654f969f5dec340071bb30e8d7621038fb3fa5e77dafd24c09d819dbdc7c11dca55b350511bf3bc499c09316a89286e55ae",
|
|
||||||
"372BzC1GGjziT8zGYbryja3kF2KaTeobRK": "53210214ec25e5cb42e51883d4e615316748feefe91133fcfc0f93f0b5a24a55e0a347210262336210b3173aa4ca90d292990f463e42bdeb2e73112925dc712c5a2e749bcb210277517855f512564f225e63c650dad7720565aa563901d50743be4b0f0267dcc72102d2777a9faf5d2e2b2363e1270d61021bc2e36e8cb19ca9d29dedbba9a0348b532103a57db80b6ae573e5cef2115e36e73e5ef41f8a099bfb5087d80320c04a7db72e55ae"
|
|
||||||
},
|
|
||||||
"selectedUtxos": [{
|
|
||||||
"address": "3E1d1z7gJSFeZM2d3k12MJ1jGwrBRy1YTw",
|
|
||||||
"scriptPubKey": "a91487264aa41e3df76f3156c7fa587fd7d5b1f7b96b87",
|
|
||||||
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
|
|
||||||
"vout": 1,
|
|
||||||
"amount": 10,
|
|
||||||
"confirmations": 7
|
|
||||||
}],
|
|
||||||
"inputsSigned": 0,
|
|
||||||
"signaturesAdded": 1,
|
|
||||||
"signhash": 1,
|
|
||||||
"spendUnconfirmed": false,
|
|
||||||
"tx": "0100000001c1cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a01000000fd40010047304402201aad0ea92f929be21d60afb741b76bfcf2aa9614079cb9da76b15b3a1210f07b02205bc5c1895105da3ee682e532d2d49dfd1214fa6123fb9c9d552336f135c77eff0147304402203f02f15bb4ad4bc7d0ca4612fd9e9ae05a1331f3d62f302cf08dba56695f5dcf0220700110562fe228ebba324b120de64a7bbaddf049d67a313f749e9b32ee88787d014cad5321027da98ce0407138461f4ad803a1fb67afa4246f06ad0e1256a087daeffd82a8642102e7f87f2b094ec322be2fb99d3ca81fd6ac0ab26ce081e51ab18c507f8b6d1d162102ed5d256036e10568c33e0a15021cc1806e04d7d24c8f020aaae23ec2deecb4302103b6f88231bb5a5f4629e4d097492321f86968c6aeb012196a1fe3b217fe4ae0ac2103f6b5e2c1db3124a5058a0e3e83d5d0c412b9a5b50e2ef97e2b7e1c0af57ab3e355aeffffffff0215cd5b07000000001976a91434f8e0c5be216025a52addf18a987543cad23f7a88acdbd53e340000000017a914a09f10bc42b61ecf9d3d09550765c228f1cb808a8700000000"
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
"networkName": "testnet"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
w._handleTxProposals('senderID', txps, true);
|
w._handleTxProposal('senderID', txp, true);
|
||||||
Object.keys(w.txProposals.txps).length.should.equal(1);
|
Object.keys(w.txProposals.txps).length.should.equal(1);
|
||||||
w.getTxProposals().length.should.equal(1);
|
w.getTxProposals().length.should.equal(1);
|
||||||
});
|
});
|
||||||
|
@ -514,7 +505,6 @@ describe('Wallet model', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// tx handling
|
// tx handling
|
||||||
|
|
||||||
var createUTXO = function(w) {
|
var createUTXO = function(w) {
|
||||||
|
@ -577,7 +567,6 @@ describe('Wallet model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should create & sign & send a transaction', function(done) {
|
it('should create & sign & send a transaction', function(done) {
|
||||||
|
|
||||||
var w = createW2(null, 1);
|
var w = createW2(null, 1);
|
||||||
var utxo = createUTXO(w);
|
var utxo = createUTXO(w);
|
||||||
w.blockchain.fixUnspent(utxo);
|
w.blockchain.fixUnspent(utxo);
|
||||||
|
@ -588,9 +577,113 @@ describe('Wallet model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('#getNetworkName', function() {
|
it('should send TxProposal', function(done) {
|
||||||
var w = createW();
|
var w = createW2();
|
||||||
w.getNetworkName().should.equal('testnet');
|
var utxo = createUTXO(w);
|
||||||
|
w.blockchain.fixUnspent(utxo);
|
||||||
|
w.createTx(toAddress, amountSatStr, null, function(ntxid) {
|
||||||
|
w.sendTxProposal.bind(w).should.throw('Illegal Argument.');
|
||||||
|
(function() {
|
||||||
|
w.sendTxProposal(ntxid);
|
||||||
|
}).should.not.throw();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should send all TxProposal', function(done) {
|
||||||
|
var w = createW2();
|
||||||
|
var utxo = createUTXO(w);
|
||||||
|
w.blockchain.fixUnspent(utxo);
|
||||||
|
w.createTx(toAddress, amountSatStr, null, function(ntxid) {
|
||||||
|
w.sendAllTxProposals.bind(w).should.not.throw();
|
||||||
|
(function() {
|
||||||
|
w.sendAllTxProposals();
|
||||||
|
}).should.not.throw();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var mockFakeActivity = function(w, f) {
|
||||||
|
var ADDRESSES_CHANGE = w.deriveAddresses(0, 20, true);
|
||||||
|
var ADDRESSES_RECEIVE = w.deriveAddresses(0, 20, false);
|
||||||
|
w.blockchain.checkActivity = function(addresses, cb) {
|
||||||
|
var activity = new Array(addresses.length);
|
||||||
|
for(var i=0; i<addresses.length; i++) {
|
||||||
|
var a1 = ADDRESSES_CHANGE.indexOf(addresses[i]);
|
||||||
|
var a2 = ADDRESSES_RECEIVE.indexOf(addresses[i]);
|
||||||
|
activity[i] = f(Math.max(a1, a2));
|
||||||
|
}
|
||||||
|
cb(null, activity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it('#indexDiscovery should work without found activities', function(done) {
|
||||||
|
var w = createW2();
|
||||||
|
mockFakeActivity(w, function(index) { return false });
|
||||||
|
w.indexDiscovery(0, false, 5, function(e, lastActive){
|
||||||
|
lastActive.should.equal(-1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#indexDiscovery should continue scanning', function(done) {
|
||||||
|
var w = createW2();
|
||||||
|
mockFakeActivity(w, function(index) { return index <= 7 });
|
||||||
|
w.indexDiscovery(0, false, 5, function(e, lastActive){
|
||||||
|
lastActive.should.equal(7);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#indexDiscovery should not found beyond the scannWindow', function(done) {
|
||||||
|
var w = createW2();
|
||||||
|
mockFakeActivity(w, function(index) { return index <= 10 || index == 17 });
|
||||||
|
w.indexDiscovery(0, false, 5, function(e, lastActive){
|
||||||
|
lastActive.should.equal(10);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#indexDiscovery should look for activity along the scannWindow', function(done) {
|
||||||
|
var w = createW2();
|
||||||
|
mockFakeActivity(w, function(index) { return index <= 14 && index % 2 == 0 });
|
||||||
|
w.indexDiscovery(0, false, 5, function(e, lastActive){
|
||||||
|
lastActive.should.equal(14);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#updateIndexes should update correctly', function(done) {
|
||||||
|
var w = createW2();
|
||||||
|
mockFakeActivity(w, function(index) { return index <= 14 && index % 2 == 0 });
|
||||||
|
w.updateIndexes(function(err) {
|
||||||
|
w.publicKeyRing.indexes.receiveIndex.should.equal(15);
|
||||||
|
w.publicKeyRing.indexes.changeIndex.should.equal(15);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#updateIndexes should store and emit event', function(done) {
|
||||||
|
var w = createW2();
|
||||||
|
mockFakeActivity(w, function(index) { return index <= 14 && index % 2 == 0 });
|
||||||
|
var spyStore = sinon.spy(w, 'store');
|
||||||
|
var spyEmit = sinon.spy(w, 'emit');
|
||||||
|
w.updateIndexes(function(err) {
|
||||||
|
sinon.assert.callCount(spyStore, 1);
|
||||||
|
sinon.assert.callCount(spyEmit, 1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#deriveAddresses', function(done) {
|
||||||
|
var w = createW2();
|
||||||
|
var addresses1 = w.deriveAddresses(0, 5, false);
|
||||||
|
var addresses2 = w.deriveAddresses(4, 5, false);
|
||||||
|
|
||||||
|
addresses1.length.should.equal(5);
|
||||||
|
addresses2.length.should.equal(5);
|
||||||
|
|
||||||
|
addresses1[4].should.equal(addresses2[0]);
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -125,4 +125,35 @@ describe('Insight model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it('#checkActivity for innactive addreses', function(done) {
|
||||||
|
var w = new Insight();
|
||||||
|
w.getTransactions = function(addresses, cb) {
|
||||||
|
cb([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
w.checkActivity(addresses, function(err, actives){
|
||||||
|
console.log(err);
|
||||||
|
actives.length.should.equal(addresses.length);
|
||||||
|
actives.filter(function(i) { return i }).length.should.equal(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('#checkActivity for active addreses', function(done) {
|
||||||
|
var w = new Insight();
|
||||||
|
w.getTransactions = function(addresses, cb) {
|
||||||
|
cb([
|
||||||
|
{vin: [{ addr: '2NATQJnaQe2CUKLyhL1zdNkttJM1dUH9HaM'}], vout: []},
|
||||||
|
{vin: [{ addr: '2NATQJnaQe2CUKLyhL1zdNkttJM1dUH9HaM'}], vout: []},
|
||||||
|
{vin: [{ addr: '2N9D5bcCQ2bPWUDByQ6Qb5bMgMtgsk1rw3x'}], vout: []},
|
||||||
|
{vin: [], vout: [{scriptPubKey: {addresses: ['2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY']}}]}
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
w.checkActivity(addresses, function(err, actives){
|
||||||
|
actives.length.should.equal(addresses.length);
|
||||||
|
actives.filter(function(i) { return i }).length.should.equal(3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
var sinon = require('sinon');
|
||||||
|
|
||||||
describe('Check config', function() {
|
describe('Check config', function() {
|
||||||
it('unit should be set to BITS in config.js', function() {
|
it('unit should be set to BITS in config.js', function() {
|
||||||
expect(config.unitToSatoshi).to.equal(100);
|
expect(config.unitToSatoshi).to.equal(100);
|
||||||
|
@ -87,9 +89,14 @@ describe("Unit: controllerUtils", function() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Unit: Notification Service", function() {
|
||||||
|
beforeEach(angular.mock.module('copayApp.services'));
|
||||||
|
it('should contain a notification service', inject(function(notification) {
|
||||||
|
expect(notification).not.to.equal(null);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
describe("Unit: Backup Service", function() {
|
describe("Unit: Backup Service", function() {
|
||||||
var sinon = require('sinon');
|
|
||||||
beforeEach(angular.mock.module('copayApp.services'));
|
beforeEach(angular.mock.module('copayApp.services'));
|
||||||
it('should contain a backup service', inject(function(backupService) {
|
it('should contain a backup service', inject(function(backupService) {
|
||||||
expect(backupService).not.to.equal(null);
|
expect(backupService).not.to.equal(null);
|
||||||
|
|
Loading…
Reference in New Issue