fix conflics
This commit is contained in:
commit
44901cd635
19 changed files with 560 additions and 350 deletions
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('HeaderController',
|
||||
function($scope, $rootScope, $location, $notification, $http, controllerUtils) {
|
||||
function($scope, $rootScope, $location, notification, $http, controllerUtils) {
|
||||
$scope.menu = [
|
||||
{
|
||||
'title': 'Addresses',
|
||||
|
|
@ -64,7 +64,7 @@ angular.module('copayApp.controllers').controller('HeaderController',
|
|||
}
|
||||
if (currentAddr) {
|
||||
//var beep = new Audio('sound/transaction.mp3');
|
||||
$notification.funds('Received fund', currentAddr, receivedFund);
|
||||
notification.funds('Received fund', currentAddr, receivedFund);
|
||||
//beep.play();
|
||||
}
|
||||
}
|
||||
|
|
@ -72,7 +72,7 @@ angular.module('copayApp.controllers').controller('HeaderController',
|
|||
|
||||
$rootScope.$watch('txAlertCount', function(txAlertCount) {
|
||||
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 _importBackup = function(encryptedObj) {
|
||||
Passphrase.getBase64Async($scope.password, function(passphrase){
|
||||
var w, errMsg;
|
||||
try {
|
||||
w = walletFactory.fromEncryptedObj(encryptedObj, passphrase);
|
||||
} catch(e) {
|
||||
errMsg = e.message;
|
||||
}
|
||||
if (!w) {
|
||||
$scope.loading = false;
|
||||
$rootScope.$flashMessage = { message: errMsg || 'Wrong password', type: 'error'};
|
||||
$rootScope.$digest();
|
||||
return;
|
||||
}
|
||||
$rootScope.wallet = w;
|
||||
|
||||
controllerUtils.startNetwork($rootScope.wallet, $scope);
|
||||
walletFactory.import(encryptedObj, passphrase, function(err, w) {
|
||||
if (err) {
|
||||
$scope.loading = false;
|
||||
$rootScope.$flashMessage = { message: err.errMsg || 'Wrong password', type: 'error'};
|
||||
$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) {
|
||||
if (options.method === 'POST') {
|
||||
options.headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Length': options.data.length,
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ AddressIndex.prototype.toObj = function() {
|
|||
AddressIndex.prototype.checkRange = function(index, isChange) {
|
||||
if ((isChange && index > this.changeIndex) ||
|
||||
(!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
|
||||
PublicKeyRing.prototype.getRedeemScript = function (index, isChange) {
|
||||
this.indexes.checkRange(index, isChange);
|
||||
|
||||
var pubKeys = this.getPubKeys(index, isChange);
|
||||
var script = Script.createMultisig(this.requiredCopayers, pubKeys);
|
||||
return script;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@ function TxProposal(opts) {
|
|||
this.comment = opts.comment || null;
|
||||
}
|
||||
|
||||
TxProposal.prototype.getID = function() {
|
||||
var ntxid = this.builder.build().getNormalizedHash().toString('hex');
|
||||
return ntxid;
|
||||
};
|
||||
|
||||
TxProposal.prototype.toObj = function() {
|
||||
var o = JSON.parse(JSON.stringify(this));
|
||||
delete o['builder'];
|
||||
|
|
@ -46,6 +51,76 @@ TxProposal.getSentTs = function() {
|
|||
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);
|
||||
|
||||
|
||||
|
|
@ -75,6 +150,7 @@ TxProposals.prototype.getNtxids = function() {
|
|||
};
|
||||
|
||||
TxProposals.prototype.toObj = function(onlyThisNtxid) {
|
||||
if (onlyThisNtxid) throw new Error();
|
||||
var ret = [];
|
||||
for (var id in this.txps) {
|
||||
|
||||
|
|
@ -92,114 +168,37 @@ TxProposals.prototype.toObj = function(onlyThisNtxid) {
|
|||
};
|
||||
};
|
||||
|
||||
TxProposals.prototype._startMerge = function(myTxps, theirTxps) {
|
||||
var fromUs = 0,
|
||||
fromTheirs = 0,
|
||||
merged = 0;
|
||||
var toMerge = {},
|
||||
ready = {},
|
||||
events = [];
|
||||
TxProposals.prototype.merge = function(inTxp) {
|
||||
var myTxps = this.txps;
|
||||
|
||||
for (var hash in theirTxps) {
|
||||
if (!myTxps[hash]) {
|
||||
ready[hash] = theirTxps[hash]; // only in theirs;
|
||||
events.push({type: 'new', cid: theirTxps[hash].creator, tx: hash});
|
||||
fromTheirs++;
|
||||
} else {
|
||||
toMerge[hash] = theirTxps[hash]; // need Merging
|
||||
merged++;
|
||||
}
|
||||
}
|
||||
var ntxid = inTxp.getID();
|
||||
var ret = {};
|
||||
ret.events = [];
|
||||
ret.events.hasChanged = false;
|
||||
|
||||
for (var hash in myTxps) {
|
||||
if (!toMerge[hash]) {
|
||||
ready[hash] = myTxps[hash]; // only in myTxps;
|
||||
fromUs++;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
stats: {
|
||||
fromUs: fromUs,
|
||||
fromTheirs: fromTheirs,
|
||||
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});
|
||||
}
|
||||
if (myTxps[ntxid]) {
|
||||
var v0 = myTxps[ntxid];
|
||||
var v1 = inTxp;
|
||||
ret = v0.merge(v1);
|
||||
} else {
|
||||
this.txps[ntxid] = inTxp;
|
||||
ret.hasChanged = true;
|
||||
ret.events.push({
|
||||
type: 'new',
|
||||
cid: inTxp.creator,
|
||||
tx: 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: 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) {
|
||||
var ntxid = data.builder.build().getNormalizedHash().toString('hex');
|
||||
this.txps[ntxid] = new TxProposal(data);
|
||||
var txp = new TxProposal(data);
|
||||
var ntxid = txp.getID();
|
||||
this.txps[ntxid] = txp;
|
||||
return ntxid;
|
||||
};
|
||||
|
||||
|
||||
TxProposals.prototype.setSent = function(ntxid, txid) {
|
||||
//sent TxProposals are local an not broadcasted.
|
||||
this.txps[ntxid].setSent(txid);
|
||||
|
|
@ -259,26 +258,5 @@ TxProposals.prototype.getUsedUnspent = function(maxRejectCount) {
|
|||
return ret;
|
||||
};
|
||||
|
||||
TxProposals.prototype.merge = function(t) {
|
||||
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;
|
||||
};
|
||||
|
||||
TxProposals.TxProposal = TxProposal;
|
||||
module.exports = require('soop')(TxProposals);
|
||||
|
|
|
|||
|
|
@ -2,17 +2,24 @@
|
|||
|
||||
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 bignum = bitcore.Bignum;
|
||||
var coinUtil = bitcore.util;
|
||||
var buffertools = bitcore.buffertools;
|
||||
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 Base58Check = bitcore.Base58.base58Check;
|
||||
|
||||
var AddressIndex = require('./AddressIndex');
|
||||
var PublicKeyRing = require('./PublicKeyRing');
|
||||
var TxProposals = require('./TxProposals');
|
||||
var PrivateKey = require('./PrivateKey');
|
||||
|
||||
function Wallet(opts) {
|
||||
var self = this;
|
||||
|
||||
|
|
@ -74,7 +81,7 @@ Wallet.prototype.connectToAll = function() {
|
|||
|
||||
Wallet.prototype._handleIndexes = function(senderId, data, isInbound) {
|
||||
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);
|
||||
if (hasChanged) {
|
||||
this.emit('publicKeyRingUpdated');
|
||||
|
|
@ -85,7 +92,7 @@ Wallet.prototype._handleIndexes = function(senderId, data, isInbound) {
|
|||
Wallet.prototype._handlePublicKeyRing = function(senderId, data, isInbound) {
|
||||
this.log('RECV PUBLICKEYRING:', data);
|
||||
|
||||
var inPKR = copay.PublicKeyRing.fromObj(data.publicKeyRing);
|
||||
var inPKR = PublicKeyRing.fromObj(data.publicKeyRing);
|
||||
var wasIncomplete = !this.publicKeyRing.isComplete();
|
||||
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);
|
||||
|
||||
var recipients;
|
||||
var inTxp = copay.TxProposals.fromObj(data.txProposals);
|
||||
var ids = inTxp.getNtxids();
|
||||
var inTxp = TxProposals.TxProposal.fromObj(data.txProposal);
|
||||
|
||||
if (ids.length > 1) {
|
||||
this.emit('badMessage', senderId);
|
||||
this.log('Received BAD TxProposal messsage FROM:', senderId); //TODO
|
||||
return;
|
||||
}
|
||||
var mergeInfo = this.txProposals.merge(inTxp);
|
||||
var added = this.addSeenToTxProposals();
|
||||
|
||||
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++) {
|
||||
this.emit('txProposalEvent', mergeInfo.events[i]);
|
||||
}
|
||||
|
|
@ -156,13 +149,13 @@ Wallet.prototype._handleData = function(senderId, data, isInbound) {
|
|||
break;
|
||||
case 'walletReady':
|
||||
this.sendPublicKeyRing(senderId);
|
||||
this.sendTxProposals(senderId); // send old
|
||||
this.sendAllTxProposals(senderId); // send old txps
|
||||
break;
|
||||
case 'publicKeyRing':
|
||||
this._handlePublicKeyRing(senderId, data, isInbound);
|
||||
break;
|
||||
case 'txProposals':
|
||||
this._handleTxProposals(senderId, data, isInbound);
|
||||
case 'txProposal':
|
||||
this._handleTxProposal(senderId, data, isInbound);
|
||||
break;
|
||||
case 'indexes':
|
||||
this._handleIndexes(senderId, data, isInbound);
|
||||
|
|
@ -340,9 +333,9 @@ Wallet.prototype.toObj = function() {
|
|||
|
||||
Wallet.fromObj = function(o, storage, network, blockchain) {
|
||||
var opts = JSON.parse(JSON.stringify(o.opts));
|
||||
opts.publicKeyRing = copay.PublicKeyRing.fromObj(o.publicKeyRing);
|
||||
opts.txProposals = copay.TxProposals.fromObj(o.txProposals);
|
||||
opts.privateKey = copay.PrivateKey.fromObj(o.privateKey);
|
||||
opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing);
|
||||
opts.txProposals = TxProposals.fromObj(o.txProposals);
|
||||
opts.privateKey = PrivateKey.fromObj(o.privateKey);
|
||||
|
||||
opts.storage = storage;
|
||||
opts.network = network;
|
||||
|
|
@ -358,25 +351,25 @@ Wallet.prototype.toEncryptedObj = function() {
|
|||
return this.storage.export(walletObj);
|
||||
};
|
||||
|
||||
Wallet.prototype.sendTxProposals = function(recipients, ntxid) {
|
||||
this.log('### SENDING txProposals TO:', recipients || 'All', this.txProposals);
|
||||
|
||||
var toSend = ntxid ? [ntxid] : this.txProposals.getNtxids();
|
||||
|
||||
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.sendAllTxProposals = function(recipients) {
|
||||
var ntxids = this.txProposals.getNtxids();
|
||||
for (var i in ntxids) {
|
||||
var ntxid = ntxids[i];
|
||||
this.sendTxProposal(ntxid, recipients);
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
this.log('### SENDING WalletReady TO:', recipients);
|
||||
|
||||
|
|
@ -440,14 +433,15 @@ Wallet.prototype.generateAddress = function(isChange, cb) {
|
|||
Wallet.prototype.getTxProposals = function() {
|
||||
var ret = [];
|
||||
var copayers = this.getRegisteredCopayerIds();
|
||||
for (var k in this.txProposals.txps) {
|
||||
var i = this.txProposals.getTxProposal(k, copayers);
|
||||
i.signedByUs = i.signedBy[this.getMyCopayerId()] ? true : false;
|
||||
i.rejectedByUs = i.rejectedBy[this.getMyCopayerId()] ? true : false;
|
||||
if (this.totalCopayers - i.rejectCount < this.requiredCopayers)
|
||||
i.finallyRejected = true;
|
||||
for (var ntxid in this.txProposals.txps) {
|
||||
var txp = this.txProposals.getTxProposal(ntxid, copayers);
|
||||
txp.signedByUs = txp.signedBy[this.getMyCopayerId()] ? true : false;
|
||||
txp.rejectedByUs = txp.rejectedBy[this.getMyCopayerId()] ? true : false;
|
||||
if (this.totalCopayers - txp.rejectCount < this.requiredCopayers) {
|
||||
txp.finallyRejected = true;
|
||||
}
|
||||
|
||||
ret.push(i);
|
||||
ret.push(txp);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
|
@ -461,7 +455,7 @@ Wallet.prototype.reject = function(ntxid) {
|
|||
}
|
||||
|
||||
txp.rejectedBy[myId] = Date.now();
|
||||
this.sendTxProposals(null, ntxid);
|
||||
this.sendTxProposal(ntxid);
|
||||
this.store();
|
||||
this.emit('txProposalsUpdated');
|
||||
};
|
||||
|
|
@ -486,7 +480,7 @@ Wallet.prototype.sign = function(ntxid, cb) {
|
|||
var ret = false;
|
||||
if (b.signaturesAdded > before) {
|
||||
txp.signedBy[myId] = Date.now();
|
||||
self.sendTxProposals(null, ntxid);
|
||||
self.sendTxProposal(ntxid);
|
||||
self.store();
|
||||
self.emit('txProposalsUpdated');
|
||||
ret = true;
|
||||
|
|
@ -514,7 +508,7 @@ Wallet.prototype.sendTx = function(ntxid, cb) {
|
|||
self.log('BITCOIND txid:', txid);
|
||||
if (txid) {
|
||||
self.txProposals.setSent(ntxid, txid);
|
||||
self.sendTxProposals(null, ntxid);
|
||||
self.sendTxProposal(ntxid);
|
||||
self.store();
|
||||
}
|
||||
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);
|
||||
if (ntxid) {
|
||||
self.sendIndexes();
|
||||
self.sendTxProposals(null, ntxid);
|
||||
self.sendTxProposal(ntxid);
|
||||
self.store();
|
||||
self.emit('txProposalsUpdated');
|
||||
}
|
||||
|
|
@ -711,6 +705,71 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
|
|||
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() {
|
||||
this.log('## DISCONNECTING');
|
||||
this.network.disconnect();
|
||||
|
|
|
|||
|
|
@ -74,6 +74,16 @@ WalletFactory.prototype.fromEncryptedObj = function(base64, password) {
|
|||
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) {
|
||||
if (!this._checkRead(walletId))
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
'use strict';
|
||||
|
||||
var BackupService = function($notification) {
|
||||
this.notifications = $notification;
|
||||
|
||||
var BackupService = function(notification) {
|
||||
this.notifications = notification;
|
||||
};
|
||||
|
||||
BackupService.prototype.getName = function(wallet) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
var bitcore = require('bitcore');
|
||||
|
||||
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 = {};
|
||||
|
||||
root.getVideoMutedStatus = function(copayer) {
|
||||
|
|
@ -87,7 +87,7 @@ angular.module('copayApp.services')
|
|||
$rootScope.$digest();
|
||||
};
|
||||
|
||||
$notification.enableHtml5Mode(); // for chrome: if support, enable it
|
||||
notification.enableHtml5Mode(); // for chrome: if support, enable it
|
||||
|
||||
w.on('badMessage', function(peerId) {
|
||||
$rootScope.$flashMessage = {
|
||||
|
|
@ -126,11 +126,11 @@ angular.module('copayApp.services')
|
|||
switch (e.type) {
|
||||
case 'signed':
|
||||
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;
|
||||
case 'rejected':
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
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 = [];
|
||||
|
||||
var settings = {
|
||||
|
|
@ -186,7 +186,7 @@ angular.module('copayApp.services').
|
|||
save: function(){
|
||||
// Save all the notifications into 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
|
||||
|
|
@ -245,7 +245,7 @@ angular.module('copayApp.services').
|
|||
template: html,
|
||||
link: link,
|
||||
controller: ['$scope', function NotificationsCtrl( $scope ){
|
||||
$scope.queue = $notification.getQueue();
|
||||
$scope.queue = notification.getQueue();
|
||||
|
||||
$scope.removeNotification = function(noti){
|
||||
$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.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) {
|
||||
|
|
@ -64,7 +66,7 @@ Video.prototype._addCall = function(mediaConnection, cb) {
|
|||
|
||||
// Wait for stream on the call, then set peer video display
|
||||
mediaConnection.on('stream', function(stream) {
|
||||
self.onlineSound.play();
|
||||
if (self.onlineSound) self.onlineSound.play();
|
||||
cb(null, peerID, URL.createObjectURL(stream));
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue