mirror of https://github.com/BTCPrivate/copay.git
commit
e17aed54b0
35
index.html
35
index.html
|
@ -178,7 +178,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/ng-template" id="peer.html">
|
<script type="text/ng-template" id="peer.html">
|
||||||
<div class="row" ng-controller="PeerController">
|
<div class="row">
|
||||||
<div class="large-6 columns p70r line-dashed-v">
|
<div class="large-6 columns p70r line-dashed-v">
|
||||||
<h3>I am </h3>
|
<h3>I am </h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -244,16 +244,18 @@
|
||||||
<div class="transactions" data-ng-controller="TransactionsController">
|
<div class="transactions" data-ng-controller="TransactionsController">
|
||||||
<div class="row" ng-show='$root.wallet.publicKeyRing.isComplete()'>
|
<div class="row" ng-show='$root.wallet.publicKeyRing.isComplete()'>
|
||||||
<div class="large-12 columns">
|
<div class="large-12 columns">
|
||||||
<h4>Pending transactions <small>({{txs.length}})</small></h4>
|
<h4>Transactions proposals <small>({{txs.length}})</small></h4>
|
||||||
<div class="panel pending" ng-repeat="tx in txs | orderBy: 'createdTs':true">
|
<div class="panel pending" ng-repeat="tx in txs | orderBy: 'createdTs':true">
|
||||||
<div class="txheader">
|
<div class="txheader">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="large-8 columns">
|
<div class="large-8 columns">
|
||||||
<tr ng-repeat="o in tx.outs">
|
<table style="width:100%">
|
||||||
<td class="text-right size-24">{{o.value}} <i class="fi-bitcoin"></i></td>
|
<tr ng-repeat="out in tx.outs">
|
||||||
<td class="text-center size-48"> <i class="fi-arrow-right size-40"> </i>
|
<td class="text-right size-24"> <i class="fi-bitcoin"></i> {{out.value}}</td>
|
||||||
<td class="text-left size-24">{{o.address}}</td>
|
<td class="text-center size-48"> <i class="fi-arrow-right size-24"> </i></td>
|
||||||
|
<td class="text-left size-24">{{out.address}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="large-4 columns text-right">
|
<div class="large-4 columns text-right">
|
||||||
<h6> created at {{tx.createdTs | date:'medium'}} </h6>
|
<h6> created at {{tx.createdTs | date:'medium'}} </h6>
|
||||||
|
@ -266,7 +268,10 @@
|
||||||
<table style="width:100%">
|
<table style="width:100%">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<small class="right text-gray"> <strong> Proposal ID: </strong> {{tx.ntxid}} </small>
|
<small class="right text-gray">
|
||||||
|
Fee: <strong> <i class="fi-bitcoin"></i> {{tx.fee}} </strong>
|
||||||
|
<strong> Proposal ID: </strong> {{tx.ntxid}}
|
||||||
|
</small>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-repeat="(peer, actions) in tx.peerActions">
|
<tr ng-repeat="(peer, actions) in tx.peerActions">
|
||||||
<td colspan="3">
|
<td colspan="3">
|
||||||
|
@ -279,6 +284,7 @@
|
||||||
<span ng-if="action == 'create'" title="{{ts | date:'medium'}}" class="fi-page-add size-18 right m10h"></span>
|
<span ng-if="action == 'create'" title="{{ts | date:'medium'}}" class="fi-page-add size-18 right m10h"></span>
|
||||||
<span ng-if="action == 'seen'" title="{{ts | date:'medium'}}" class="fi-eye size-18 right m10h"> </span>
|
<span ng-if="action == 'seen'" title="{{ts | date:'medium'}}" class="fi-eye size-18 right m10h"> </span>
|
||||||
<span ng-if="action == 'sign'" title="{{ts | date:'medium'}}" class="fi-check size-18 right m10h"> </span>
|
<span ng-if="action == 'sign'" title="{{ts | date:'medium'}}" class="fi-check size-18 right m10h"> </span>
|
||||||
|
<span ng-if="action == 'rejected'" title="{{ts | date:'medium'}}" class="fi-x size-18 right m10h" style="color:red"> </span>
|
||||||
|
|
||||||
<a title="{{ts | date:'medium'}}"></a>
|
<a title="{{ts | date:'medium'}}"></a>
|
||||||
</span>
|
</span>
|
||||||
|
@ -296,16 +302,20 @@
|
||||||
<div ng-show="tx.signedByUs">
|
<div ng-show="tx.signedByUs">
|
||||||
<i class="fi-check size-40"></i> Signed by you already
|
<i class="fi-check size-40"></i> Signed by you already
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="!tx.signedByUs && tx.missingSignatures">
|
<div ng-show="!tx.signedByUs && !tx.rejectedByUs && !tx.finallyRejected && tx.missingSignatures">
|
||||||
<button class="secondary round" ng-click="sign(tx.ntxid)">
|
<button class="secondary round" ng-click="sign(tx.ntxid)">
|
||||||
<i class="fi-check"></i> Sign
|
<i class="fi-check"></i> Sign
|
||||||
</button>
|
</button>
|
||||||
<button class="primary round">
|
<button class="primary round" ng-click="reject(tx.ntxid)">
|
||||||
<i class="fi-x"></i> Ignore
|
<i class="fi-x" ></i> Reject
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="large-6 columns text-right">
|
<div class="large-6 columns text-right">
|
||||||
|
<span ng-show="tx.finallyRejected ">
|
||||||
|
Transaction finally rejected
|
||||||
|
</span>
|
||||||
|
|
||||||
<span ng-show="!tx.missingSignatures && !tx.sentTs">
|
<span ng-show="!tx.missingSignatures && !tx.sentTs">
|
||||||
Transaction ready.
|
Transaction ready.
|
||||||
<button class="secondary round" ng-click="send(tx.ntxid)">
|
<button class="secondary round" ng-click="send(tx.ntxid)">
|
||||||
|
@ -318,10 +328,10 @@
|
||||||
Sent at {{tx.sentTs | date:'medium'}}
|
Sent at {{tx.sentTs | date:'medium'}}
|
||||||
TXID: {{tx.sentTxid}}
|
TXID: {{tx.sentTxid}}
|
||||||
</span>
|
</span>
|
||||||
<span ng-show="tx.missingSignatures==1">
|
<span ng-show="!tx.finallyRejected && tx.missingSignatures==1">
|
||||||
One signature missing
|
One signature missing
|
||||||
</span>
|
</span>
|
||||||
<span ng-show="tx.missingSignatures>1">
|
<span ng-show="!tx.finallyRejected && tx.missingSignatures>1">
|
||||||
{{tx.missingSignatures}} signatures missing</span>
|
{{tx.missingSignatures}} signatures missing</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -497,7 +507,6 @@
|
||||||
<script src="js/controllers/backup.js"></script>
|
<script src="js/controllers/backup.js"></script>
|
||||||
<script src="js/controllers/signin.js"></script>
|
<script src="js/controllers/signin.js"></script>
|
||||||
<script src="js/controllers/setup.js"></script>
|
<script src="js/controllers/setup.js"></script>
|
||||||
<script src="js/controllers/peer.js"></script>
|
|
||||||
|
|
||||||
<script src="js/init.js"></script>
|
<script src="js/init.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -13,8 +13,7 @@ angular.module('copay',[
|
||||||
'copay.signin',
|
'copay.signin',
|
||||||
'copay.socket',
|
'copay.socket',
|
||||||
'copay.controllerUtils',
|
'copay.controllerUtils',
|
||||||
'copay.setup',
|
'copay.setup'
|
||||||
'copay.peer'
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
angular.module('copay.header', []);
|
angular.module('copay.header', []);
|
||||||
|
@ -26,6 +25,5 @@ angular.module('copay.walletFactory', []);
|
||||||
angular.module('copay.controllerUtils', []);
|
angular.module('copay.controllerUtils', []);
|
||||||
angular.module('copay.signin', []);
|
angular.module('copay.signin', []);
|
||||||
angular.module('copay.setup', []);
|
angular.module('copay.setup', []);
|
||||||
angular.module('copay.peer', []);
|
|
||||||
angular.module('copay.socket', []);
|
angular.module('copay.socket', []);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
angular.module('copay.peer').controller('PeerController',
|
|
||||||
function($scope, $rootScope, $location, $routeParams) {
|
|
||||||
});
|
|
||||||
|
|
|
@ -23,16 +23,15 @@ angular.module('copay.send').controller('SendController',
|
||||||
|
|
||||||
var w = $rootScope.wallet;
|
var w = $rootScope.wallet;
|
||||||
w.createTx( address, amount,function() {
|
w.createTx( address, amount,function() {
|
||||||
$rootScope.$digest();
|
|
||||||
});
|
|
||||||
|
|
||||||
// reset fields
|
// reset fields
|
||||||
$scope.address = null;
|
$scope.address = null;
|
||||||
$scope.amount = null;
|
$scope.amount = null;
|
||||||
form.address.$pristine = true;
|
form.address.$pristine = true;
|
||||||
form.amount.$pristine = true;
|
form.amount.$pristine = true;
|
||||||
|
$rootScope.flashMessage = { message: 'The transaction proposal has been created', type: 'success'};
|
||||||
|
$rootScope.$digest();
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: check if createTx has an error.
|
|
||||||
$rootScope.flashMessage = { message: 'Your transaction proposal has been sent successfully', type: 'success'};
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,8 +5,9 @@ angular.module('copay.transactions').controller('TransactionsController',
|
||||||
function($scope, $rootScope, $location) {
|
function($scope, $rootScope, $location) {
|
||||||
$scope.title = 'Transactions';
|
$scope.title = 'Transactions';
|
||||||
var _updateTxs = function() {
|
var _updateTxs = function() {
|
||||||
console.log('[transactions.js.10:_updateTxs:]'); //TODO
|
|
||||||
var w =$rootScope.wallet;
|
var w =$rootScope.wallet;
|
||||||
|
if (!w) return;
|
||||||
|
|
||||||
var inT = w.getTxProposals();
|
var inT = w.getTxProposals();
|
||||||
var txs = [];
|
var txs = [];
|
||||||
|
|
||||||
|
@ -25,11 +26,10 @@ console.log('[transactions.js.10:_updateTxs:]'); //TODO
|
||||||
});
|
});
|
||||||
// extra fields
|
// extra fields
|
||||||
i.outs = outs;
|
i.outs = outs;
|
||||||
i.fee = i.feeSat/bitcore.util.COIN;
|
i.fee = i.builder.feeSat/bitcore.util.COIN;
|
||||||
i.missingSignatures = tx.countInputMissingSignatures(0);
|
i.missingSignatures = tx.countInputMissingSignatures(0);
|
||||||
txs.push(i);
|
txs.push(i);
|
||||||
});
|
});
|
||||||
console.log('[transactions.js.35:txs:]',txs); //TODO
|
|
||||||
$scope.txs = txs;
|
$scope.txs = txs;
|
||||||
w.removeListener('txProposalsUpdated',_updateTxs)
|
w.removeListener('txProposalsUpdated',_updateTxs)
|
||||||
w.once('txProposalsUpdated',_updateTxs);
|
w.once('txProposalsUpdated',_updateTxs);
|
||||||
|
@ -38,7 +38,7 @@ console.log('[transactions.js.35:txs:]',txs); //TODO
|
||||||
$scope.send = function (ntxid) {
|
$scope.send = function (ntxid) {
|
||||||
var w = $rootScope.wallet;
|
var w = $rootScope.wallet;
|
||||||
w.sendTx(ntxid, function(txid) {
|
w.sendTx(ntxid, function(txid) {
|
||||||
console.log('[transactions.js.68:txid:] SENTTX CALLBACK',txid); //TODO
|
console.log('[transactions.js.68:txid:] SENTTX CALLBACK',txid); //TODO
|
||||||
$rootScope.flashMessage = txid
|
$rootScope.flashMessage = txid
|
||||||
? {type:'success', message: 'Transactions SENT! txid:' + txid}
|
? {type:'success', message: 'Transactions SENT! txid:' + txid}
|
||||||
: {type:'error', message: 'There was an error sending the Transaction'}
|
: {type:'error', message: 'There was an error sending the Transaction'}
|
||||||
|
@ -51,17 +51,20 @@ console.log('[transactions.js.68:txid:] SENTTX CALLBACK',txid); //TODO
|
||||||
$scope.sign = function (ntxid) {
|
$scope.sign = function (ntxid) {
|
||||||
var w = $rootScope.wallet;
|
var w = $rootScope.wallet;
|
||||||
var ret = w.sign(ntxid);
|
var ret = w.sign(ntxid);
|
||||||
_updateTxs();
|
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
$rootScope.flashMessage = {type:'error', message: 'There was an error signing the Transaction'};
|
||||||
|
_updateTxs();
|
||||||
|
$rootScope.$digest();
|
||||||
|
return;
|
||||||
|
}
|
||||||
var p = w.getTxProposal(ntxid);
|
var p = w.getTxProposal(ntxid);
|
||||||
if (p.txp.builder.isFullySigned()) {
|
if (p.txp.builder.isFullySigned()) {
|
||||||
$scope.send(ntxid);
|
$scope.send(ntxid);
|
||||||
|
_updateTxs();
|
||||||
|
$rootScope.$digest();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$rootScope.flashMessage = ret
|
|
||||||
? {type:'success', message: 'Transactions signed'}
|
|
||||||
: {type:'error', message: 'There was an error signing the Transaction'}
|
|
||||||
;
|
|
||||||
_updateTxs();
|
_updateTxs();
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
}
|
}
|
||||||
|
@ -79,5 +82,13 @@ console.log('[transactions.js.68:txid:] SENTTX CALLBACK',txid); //TODO
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.reject = function (ntxid) {
|
||||||
|
var w = $rootScope.wallet;
|
||||||
|
w.reject(ntxid);
|
||||||
|
$rootScope.flashMessage = {type:'warning', message: 'Transaction rejected by you'};
|
||||||
|
_updateTxs();
|
||||||
|
$rootScope.$digest();
|
||||||
|
};
|
||||||
|
|
||||||
_updateTxs();
|
_updateTxs();
|
||||||
});
|
});
|
||||||
|
|
|
@ -71,7 +71,6 @@ PrivateKey.prototype.get = function(index,isChange) {
|
||||||
|
|
||||||
PrivateKey.prototype.getAll = function(addressIndex, changeAddressIndex) {
|
PrivateKey.prototype.getAll = function(addressIndex, changeAddressIndex) {
|
||||||
var ret = [];
|
var ret = [];
|
||||||
|
|
||||||
for(var i=0;i<addressIndex; i++) {
|
for(var i=0;i<addressIndex; i++) {
|
||||||
ret.push(this.get(i,false));
|
ret.push(this.get(i,false));
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,7 +142,7 @@ PublicKeyRing.prototype.getPubKeys = function (index, isChange) {
|
||||||
var bip32 = this.copayersBIP32[i].derive(path);
|
var bip32 = this.copayersBIP32[i].derive(path);
|
||||||
pubKeys[i] = bip32.eckey.public;
|
pubKeys[i] = bip32.eckey.public;
|
||||||
}
|
}
|
||||||
this.publicKeysCache[path] = pubKeys.map(function(pk){return pk.toString('hex')});
|
this.publicKeysCache[path] = pubKeys.map(function(pk){return pk.toString('hex');});
|
||||||
} else {
|
} else {
|
||||||
pubKeys = pubKeys.map(function(s){return new Buffer(s,'hex')});
|
pubKeys = pubKeys.map(function(s){return new Buffer(s,'hex')});
|
||||||
}
|
}
|
||||||
|
@ -158,6 +158,7 @@ PublicKeyRing.prototype._checkIndexRange = function (index, isChange) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO this could be cached
|
||||||
PublicKeyRing.prototype.getRedeemScript = function (index, isChange) {
|
PublicKeyRing.prototype.getRedeemScript = function (index, isChange) {
|
||||||
this._checkIndexRange(index, isChange);
|
this._checkIndexRange(index, isChange);
|
||||||
|
|
||||||
|
@ -166,13 +167,21 @@ PublicKeyRing.prototype.getRedeemScript = function (index, isChange) {
|
||||||
return script;
|
return script;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO this could be cached
|
||||||
PublicKeyRing.prototype.getAddress = function (index, isChange) {
|
PublicKeyRing.prototype.getAddress = function (index, isChange) {
|
||||||
this._checkIndexRange(index, isChange);
|
this._checkIndexRange(index, isChange);
|
||||||
var script = this.getRedeemScript(index,isChange);
|
var script = this.getRedeemScript(index,isChange);
|
||||||
return Address.fromScript(script, this.network.name);
|
return Address.fromScript(script, this.network.name);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO this could be cached
|
||||||
|
PublicKeyRing.prototype._addScriptMap = function (map, index, isChange) {
|
||||||
|
this._checkIndexRange(index, isChange);
|
||||||
|
var script = this.getRedeemScript(index,isChange);
|
||||||
|
map[Address.fromScript(script, this.network.name).toString()] = script.getBuffer().toString('hex');
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO this could be cached
|
||||||
PublicKeyRing.prototype.getScriptPubKeyHex = function (index, isChange) {
|
PublicKeyRing.prototype.getScriptPubKeyHex = function (index, isChange) {
|
||||||
this._checkIndexRange(index, isChange);
|
this._checkIndexRange(index, isChange);
|
||||||
var addr = this.getAddress(index,isChange);
|
var addr = this.getAddress(index,isChange);
|
||||||
|
@ -180,7 +189,6 @@ PublicKeyRing.prototype.getScriptPubKeyHex = function (index, isChange) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//generate a new address, update index.
|
//generate a new address, update index.
|
||||||
PublicKeyRing.prototype.generateAddress = function(isChange) {
|
PublicKeyRing.prototype.generateAddress = function(isChange) {
|
||||||
|
|
||||||
|
@ -215,11 +223,10 @@ PublicKeyRing.prototype.getRedeemScriptMap = function () {
|
||||||
var ret = {};
|
var ret = {};
|
||||||
|
|
||||||
for (var i=0; i<this.changeAddressIndex; i++) {
|
for (var i=0; i<this.changeAddressIndex; i++) {
|
||||||
ret[this.getAddress(i,true)] = this.getRedeemScript(i,true).getBuffer().toString('hex');
|
this._addScriptMap(ret,i,true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i=0; i<this.addressIndex; i++) {
|
for (var i=0; i<this.addressIndex; i++) {
|
||||||
ret[this.getAddress(i)] = this.getRedeemScript(i).getBuffer().toString('hex');
|
this._addScriptMap(ret,i,false);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
@ -267,6 +274,8 @@ PublicKeyRing.prototype._mergePubkeys = function(inPKR) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var hasChanged = false;
|
var hasChanged = false;
|
||||||
var l= self.copayersBIP32.length;
|
var l= self.copayersBIP32.length;
|
||||||
|
if (self.isComplete())
|
||||||
|
return;
|
||||||
|
|
||||||
inPKR.copayersBIP32.forEach( function(b) {
|
inPKR.copayersBIP32.forEach( function(b) {
|
||||||
var haveIt = false;
|
var haveIt = false;
|
||||||
|
@ -279,7 +288,6 @@ PublicKeyRing.prototype._mergePubkeys = function(inPKR) {
|
||||||
}
|
}
|
||||||
if (!haveIt) {
|
if (!haveIt) {
|
||||||
if (self.isComplete()) {
|
if (self.isComplete()) {
|
||||||
//console.log('[PublicKeyRing.js.318] REPEATED KEY', epk); //TODO
|
|
||||||
throw new Error('trying to add more pubkeys, when PKR isComplete at merge');
|
throw new Error('trying to add more pubkeys, when PKR isComplete at merge');
|
||||||
}
|
}
|
||||||
self.copayersBIP32.push(new BIP32(epk));
|
self.copayersBIP32.push(new BIP32(epk));
|
||||||
|
|
|
@ -18,6 +18,7 @@ function TxProposal(opts) {
|
||||||
this.createdTs = opts.createdTs;
|
this.createdTs = opts.createdTs;
|
||||||
this.seenBy = opts.seenBy || {};
|
this.seenBy = opts.seenBy || {};
|
||||||
this.signedBy = opts.signedBy || {};
|
this.signedBy = opts.signedBy || {};
|
||||||
|
this.rejectedBy = opts.rejectedBy || {};
|
||||||
this.builder = opts.builder;
|
this.builder = opts.builder;
|
||||||
this.sentTs = opts.sentTs || null;
|
this.sentTs = opts.sentTs || null;
|
||||||
this.sentTxid = opts.sentTxid || null;
|
this.sentTxid = opts.sentTxid || null;
|
||||||
|
@ -120,6 +121,7 @@ TxProposals.prototype._startMerge = function(myTxps, theirTxps) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO add signatures.
|
||||||
TxProposals.prototype._mergeMetadata = function(myTxps, theirTxps, mergeInfo) {
|
TxProposals.prototype._mergeMetadata = function(myTxps, theirTxps, mergeInfo) {
|
||||||
|
|
||||||
var toMerge = mergeInfo.toMerge;
|
var toMerge = mergeInfo.toMerge;
|
||||||
|
@ -130,7 +132,7 @@ TxProposals.prototype._mergeMetadata = function(myTxps, theirTxps, mergeInfo) {
|
||||||
var v1 = toMerge[hash];
|
var v1 = toMerge[hash];
|
||||||
|
|
||||||
Object.keys(v1.seenBy).forEach(function(k) {
|
Object.keys(v1.seenBy).forEach(function(k) {
|
||||||
if (!v0.seenBy[k] || v0.seenBy[k] !== v1.seenBy[k]) {
|
if (!v0.seenBy[k]) {
|
||||||
v0.seenBy[k] = v1.seenBy[k];
|
v0.seenBy[k] = v1.seenBy[k];
|
||||||
hasChanged++;
|
hasChanged++;
|
||||||
}
|
}
|
||||||
|
@ -143,6 +145,13 @@ TxProposals.prototype._mergeMetadata = function(myTxps, theirTxps, mergeInfo) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Object.keys(v1.rejectedBy).forEach(function(k) {
|
||||||
|
if (!v0.rejectedBy[k]) {
|
||||||
|
v0.rejectedBy[k] = v1.rejectedBy[k];
|
||||||
|
hasChanged++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (!v0.sentTxid && v1.sentTxid) {
|
if (!v0.sentTxid && v1.sentTxid) {
|
||||||
v0.sentTs = v1.sentTs;
|
v0.sentTs = v1.sentTs;
|
||||||
v0.sentTxid = v1.sentTxid;
|
v0.sentTxid = v1.sentTxid;
|
||||||
|
@ -181,10 +190,41 @@ TxProposals.prototype.setSent = function(ntxid,txid) {
|
||||||
this.txps[ntxid].setSent(txid);
|
this.txps[ntxid].setSent(txid);
|
||||||
};
|
};
|
||||||
|
|
||||||
TxProposals.prototype.getUsedUnspent = function() {
|
|
||||||
|
TxProposals.prototype.getTxProposal = function(ntxid) {
|
||||||
|
var txp = this.txps[ntxid];
|
||||||
|
var i = JSON.parse(JSON.stringify(txp));
|
||||||
|
i.builder = txp.builder;
|
||||||
|
i.ntxid = ntxid;
|
||||||
|
i.peerActions = {};
|
||||||
|
for(var p in txp.seenBy){
|
||||||
|
i.peerActions[p]={seen: txp.seenBy[p]};
|
||||||
|
}
|
||||||
|
for(var p in txp.signedBy){
|
||||||
|
i.peerActions[p]= i.peerActions[p] || {};
|
||||||
|
i.peerActions[p].sign = txp.signedBy[p];
|
||||||
|
}
|
||||||
|
var r=0;
|
||||||
|
for(var p in txp.rejectedBy){
|
||||||
|
i.peerActions[p]= i.peerActions[p] || {};
|
||||||
|
i.peerActions[p].rejected = txp.rejectedBy[p];
|
||||||
|
r++;
|
||||||
|
}
|
||||||
|
i.rejectCount=r;
|
||||||
|
|
||||||
|
var c = txp.creator;
|
||||||
|
i.peerActions[c] = i.peerActions[c] || {};
|
||||||
|
i.peerActions[c].create = txp.createdTs;
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
|
||||||
|
TxProposals.prototype.getUsedUnspent = function(maxRejectCount) {
|
||||||
var ret = [];
|
var ret = [];
|
||||||
for(var i in this.txps) {
|
for(var i in this.txps) {
|
||||||
var u = this.txps[i].builder.getSelectedUnspent();
|
var u = this.txps[i].builder.getSelectedUnspent();
|
||||||
|
if (this.getTxProposal(i).rejectCount>maxRejectCount)
|
||||||
|
continue;
|
||||||
|
|
||||||
for (var j in u){
|
for (var j in u){
|
||||||
ret.push(u[j].txid);
|
ret.push(u[j].txid);
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,56 +269,46 @@ Wallet.prototype.sendPublicKeyRing = function(recipients) {
|
||||||
|
|
||||||
Wallet.prototype.generateAddress = function(isChange) {
|
Wallet.prototype.generateAddress = function(isChange) {
|
||||||
var addr = this.publicKeyRing.generateAddress(isChange);
|
var addr = this.publicKeyRing.generateAddress(isChange);
|
||||||
console.log('[Wallet.js.281:addr:]',addr, this.publicKeyRing.toObj(), this.getAddresses()); //TODO
|
|
||||||
this.sendPublicKeyRing();
|
this.sendPublicKeyRing();
|
||||||
this.store(true);
|
this.store(true);
|
||||||
return addr;
|
return addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO : sort by time... / signed.
|
|
||||||
Wallet.prototype.getTxProposals = function() {
|
Wallet.prototype.getTxProposals = function() {
|
||||||
var ret = [];
|
var ret = [];
|
||||||
for(var k in this.txProposals.txps) {
|
for(var k in this.txProposals.txps) {
|
||||||
var txp = this.txProposals.txps[k];
|
var i = this.txProposals.getTxProposal(k);
|
||||||
var i = JSON.parse(JSON.stringify(txp));
|
i.signedByUs = i.signedBy[this.getMyPeerId()]?true:false;
|
||||||
i.builder = txp.builder;
|
i.rejectedByUs = i.rejectedBy[this.getMyPeerId()]?true:false;
|
||||||
i.ntxid = k;
|
if (this.totalCopayers-i.rejectCount < this.requiredCopayers)
|
||||||
i.signedByUs = txp.signedBy[this.getMyPeerId()]?true:false;
|
i.finallyRejected=true;
|
||||||
|
|
||||||
i.peerActions = {};
|
|
||||||
for(var p in txp.seenBy){
|
|
||||||
i.peerActions[p]={seen: txp.seenBy[p]};
|
|
||||||
}
|
|
||||||
for(var p in txp.signedBy){
|
|
||||||
i.peerActions[p]= i.peerActions[p] || {};
|
|
||||||
i.peerActions[p].sign = txp.signedBy[p];
|
|
||||||
}
|
|
||||||
var c = txp.creator;
|
|
||||||
i.peerActions[c] = i.peerActions[c] || {};
|
|
||||||
i.peerActions[c].create = txp.createdTs;
|
|
||||||
|
|
||||||
|
|
||||||
ret.push(i);
|
ret.push(i);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.getTxProposal = function(ntxid) {
|
|
||||||
|
Wallet.prototype.reject = function(ntxid) {
|
||||||
|
var myId=this.getMyPeerId();
|
||||||
var txp = this.txProposals.txps[ntxid];
|
var txp = this.txProposals.txps[ntxid];
|
||||||
var i = {txp:txp};
|
if (!txp || txp.rejectedBy[myId] || txp.signedBy[myId]) return;
|
||||||
i.ntxid = ntxid;
|
|
||||||
i.signedByUs = txp.signedBy[this.getMyPeerId()]?true:false;
|
txp.rejectedBy[myId] = Date.now();
|
||||||
return i;
|
this.sendTxProposals();
|
||||||
|
this.store(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Wallet.prototype.sign = function(ntxid) {
|
Wallet.prototype.sign = function(ntxid) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var myId=this.getMyPeerId();
|
||||||
var txp = self.txProposals.txps[ntxid];
|
var txp = self.txProposals.txps[ntxid];
|
||||||
if (!txp) return;
|
if (!txp || txp.rejectedBy[myId] || txp.signedBy[myId]) return;
|
||||||
|
|
||||||
var pkr = self.publicKeyRing;
|
var pkr = self.publicKeyRing;
|
||||||
var keys = self.privateKey.getAll(pkr.addressIndex, pkr.changeAddressIndex);
|
var keys = self.privateKey.getAll(pkr.addressIndex, pkr.changeAddressIndex);
|
||||||
console.log('[Wallet.js.329:keys:]',keys); //TODO
|
|
||||||
|
|
||||||
var b = txp.builder;
|
var b = txp.builder;
|
||||||
var before = b.signaturesAdded;
|
var before = b.signaturesAdded;
|
||||||
|
@ -326,7 +316,7 @@ console.log('[Wallet.js.329:keys:]',keys); //TODO
|
||||||
|
|
||||||
var ret = false;
|
var ret = false;
|
||||||
if (b.signaturesAdded > before) {
|
if (b.signaturesAdded > before) {
|
||||||
txp.signedBy[self.getMyPeerId()] = Date.now();
|
txp.signedBy[myId] = Date.now();
|
||||||
this.sendTxProposals();
|
this.sendTxProposals();
|
||||||
this.store(true);
|
this.store(true);
|
||||||
ret = true;
|
ret = true;
|
||||||
|
@ -373,7 +363,6 @@ Wallet.prototype.addSeenToTxProposals = function() {
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Wallet.prototype.getAddresses = function(onlyMain) {
|
Wallet.prototype.getAddresses = function(onlyMain) {
|
||||||
return this.publicKeyRing.getAddresses(onlyMain);
|
return this.publicKeyRing.getAddresses(onlyMain);
|
||||||
};
|
};
|
||||||
|
@ -440,7 +429,8 @@ Wallet.prototype.getSafeUnspent = function(cb) {
|
||||||
this.blockchain.getUnspent(this.getAddressesStr(), function(unspentList) {
|
this.blockchain.getUnspent(this.getAddressesStr(), function(unspentList) {
|
||||||
|
|
||||||
var ret=[];
|
var ret=[];
|
||||||
var uu = self.txProposals.getUsedUnspent();
|
var maxRejectCount = self.totalCopayers - self.requiredCopayers;
|
||||||
|
var uu = self.txProposals.getUsedUnspent(maxRejectCount);
|
||||||
|
|
||||||
for(var i in unspentList){
|
for(var i in unspentList){
|
||||||
if (uu.indexOf(unspentList[i].txid) === -1)
|
if (uu.indexOf(unspentList[i].txid) === -1)
|
||||||
|
@ -465,11 +455,11 @@ Wallet.prototype.createTx = function(toAddress, amountSatStr, opts, cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.getSafeUnspent(function(unspentList) {
|
self.getSafeUnspent(function(unspentList) {
|
||||||
// TODO check enough funds, etc.
|
if (self.createTxSync(toAddress, amountSatStr, unspentList, opts)) {
|
||||||
self.createTxSync(toAddress, amountSatStr, unspentList, opts);
|
|
||||||
self.sendPublicKeyRing(); // Change Address
|
self.sendPublicKeyRing(); // Change Address
|
||||||
self.sendTxProposals();
|
self.sendTxProposals();
|
||||||
self.store();
|
self.store();
|
||||||
|
}
|
||||||
return cb();
|
return cb();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -497,25 +487,27 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, utxos, opts) {
|
||||||
|
|
||||||
var signRet;
|
var signRet;
|
||||||
if (priv) {
|
if (priv) {
|
||||||
console.log('[Wallet.js.486] aLL Priv', priv.getAll(pkr.addressIndex, pkr.changeAddressIndex)); //TODO
|
|
||||||
b.sign( priv.getAll(pkr.addressIndex, pkr.changeAddressIndex) );
|
b.sign( priv.getAll(pkr.addressIndex, pkr.changeAddressIndex) );
|
||||||
}
|
}
|
||||||
console.log('[Wallet.js.494]', b, b.build().serialize().toString('hex')); //TODO
|
|
||||||
var me = {};
|
|
||||||
var myId = this.getMyPeerId();
|
var myId = this.getMyPeerId();
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
|
|
||||||
if (priv) me[myId] = now;
|
var me = {};
|
||||||
|
if (priv && b.signaturesAdded) me[myId] = now;
|
||||||
|
|
||||||
|
var meSeen = {};
|
||||||
|
if (priv) meSeen[myId] = now;
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
signedBy: (priv && b.signaturesAdded ? me : {}),
|
signedBy: me,
|
||||||
seenBy: (priv ? me : {}),
|
seenBy: meSeen,
|
||||||
creator: myId,
|
creator: myId,
|
||||||
createdTs: now,
|
createdTs: now,
|
||||||
builder: b,
|
builder: b,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.txProposals.add(data);
|
this.txProposals.add(data);
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.connectTo = function(peerId) {
|
Wallet.prototype.connectTo = function(peerId) {
|
||||||
|
|
|
@ -261,7 +261,6 @@ console.log('[WebRTC.js.255] WARN: NO CONNECTION TO:', peerId); //TODO
|
||||||
|
|
||||||
Network.prototype.send = function(peerIds, data, cb) {
|
Network.prototype.send = function(peerIds, data, cb) {
|
||||||
var self=this;
|
var self=this;
|
||||||
console.log('[WebRTC.js.242] SENDING ', data.type); //TODO
|
|
||||||
if (!peerIds) {
|
if (!peerIds) {
|
||||||
peerIds = this.connectedPeers;
|
peerIds = this.connectedPeers;
|
||||||
data.isBroadcast = 1;
|
data.isBroadcast = 1;
|
||||||
|
|
|
@ -61,7 +61,7 @@ angular.module('copay.controllerUtils').factory('controllerUtils', function ($ro
|
||||||
console.log('### SUBSCRIBE TO', addrs[i]);
|
console.log('### SUBSCRIBE TO', addrs[i]);
|
||||||
Socket.emit('subscribe', addrs[i]);
|
Socket.emit('subscribe', addrs[i]);
|
||||||
}
|
}
|
||||||
|
console.log('[controllerUtils.js.64]'); //TODO
|
||||||
addrs.forEach(function(addr) {
|
addrs.forEach(function(addr) {
|
||||||
Socket.on(addr, function(txid) {
|
Socket.on(addr, function(txid) {
|
||||||
console.log('Received!', txid);
|
console.log('Received!', txid);
|
||||||
|
|
|
@ -70,7 +70,7 @@ describe('PrivateKey model', function() {
|
||||||
it('should calculate .id', function () {
|
it('should calculate .id', function () {
|
||||||
var w1 = new PrivateKey(config);
|
var w1 = new PrivateKey(config);
|
||||||
should.exist(w1.getId());
|
should.exist(w1.getId());
|
||||||
w1.getId().length.should.equal(40);
|
w1.getId().length.should.equal(32);
|
||||||
});
|
});
|
||||||
it('fromObj toObj roundtrip', function () {
|
it('fromObj toObj roundtrip', function () {
|
||||||
var w1 = new PrivateKey(config);
|
var w1 = new PrivateKey(config);
|
||||||
|
|
|
@ -59,9 +59,61 @@ var createPKR = function (bip32s) {
|
||||||
return w;
|
return w;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var vopts = {
|
||||||
|
verifyP2SH: true,
|
||||||
|
dontVerifyStrictEnc: true
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
describe('TxProposals model', function() {
|
describe('TxProposals model', function() {
|
||||||
|
|
||||||
|
|
||||||
|
it('verify TXs', function (done) {
|
||||||
|
|
||||||
|
var priv = new PrivateKey(config);
|
||||||
|
var priv2 = new PrivateKey(config);
|
||||||
|
var priv3 = new PrivateKey(config);
|
||||||
|
var ts = Date.now();
|
||||||
|
var isChange=0;
|
||||||
|
var index=0;
|
||||||
|
var pkr = createPKR([priv, priv2, priv3]);
|
||||||
|
var opts = {remainderOut: { address: pkr.generateAddress(true).toString() }};
|
||||||
|
|
||||||
|
var w = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = pkr.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange);
|
||||||
|
w.add(createTx(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
'123456789',
|
||||||
|
unspentTest,
|
||||||
|
opts,
|
||||||
|
priv,
|
||||||
|
pkr
|
||||||
|
));
|
||||||
|
var k = Object.keys(w.txps)[0];
|
||||||
|
var b = w.txps[k].builder;
|
||||||
|
var tx = b.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
b.sign( priv2.getAll(pkr.addressIndex, pkr.changeAddressIndex) );
|
||||||
|
b.sign( priv3.getAll(pkr.addressIndex, pkr.changeAddressIndex) );
|
||||||
|
tx = b.build();
|
||||||
|
tx.isComplete().should.equal(true);
|
||||||
|
|
||||||
|
var s = new Script(new Buffer(unspentTest[0].scriptPubKey,'hex'));
|
||||||
|
|
||||||
|
tx.verifyInput(0,s, {
|
||||||
|
verifyP2SH: true,
|
||||||
|
dontVerifyStrictEnc: true
|
||||||
|
}, function(err, results){
|
||||||
|
should.not.exist(err);
|
||||||
|
results.should.equal(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should create an instance', function () {
|
it('should create an instance', function () {
|
||||||
var w = new TxProposals({
|
var w = new TxProposals({
|
||||||
networkName: config.networkName
|
networkName: config.networkName
|
||||||
|
@ -496,5 +548,6 @@ var _dumpChunks = function (scriptSig, label) {
|
||||||
w2.merge(w);
|
w2.merge(w);
|
||||||
Object.keys(w2.txps).length.should.equal(1);
|
Object.keys(w2.txps).length.should.equal(1);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue