2014-04-10 13:57:41 -07:00
|
|
|
'use strict';
|
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
var imports = require('soop').imports();
|
2014-04-14 13:17:56 -07:00
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
var bitcore = require('bitcore');
|
|
|
|
var coinUtil = bitcore.util;
|
2014-04-14 14:30:08 -07:00
|
|
|
var buffertools = bitcore.buffertools;
|
2014-04-24 16:56:36 -07:00
|
|
|
var Builder = bitcore.TransactionBuilder;
|
|
|
|
var http = require('http');
|
|
|
|
var EventEmitter = imports.EventEmitter || require('events').EventEmitter;
|
|
|
|
var copay = copay || require('../../../copay');
|
2014-04-10 13:57:41 -07:00
|
|
|
|
2014-04-16 13:50:10 -07:00
|
|
|
function Wallet(opts) {
|
|
|
|
var self = this;
|
2014-04-14 13:17:56 -07:00
|
|
|
|
2014-04-16 13:50:10 -07:00
|
|
|
//required params
|
|
|
|
['storage', 'network', 'blockchain',
|
|
|
|
'requiredCopayers', 'totalCopayers', 'spendUnconfirmed',
|
|
|
|
'publicKeyRing', 'txProposals', 'privateKey'
|
2014-04-24 16:56:36 -07:00
|
|
|
].forEach(function(k) {
|
2014-04-16 13:50:10 -07:00
|
|
|
if (typeof opts[k] === 'undefined') throw new Error('missing key:' + k);
|
|
|
|
self[k] = opts[k];
|
|
|
|
});
|
2014-04-14 13:17:56 -07:00
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
this.log('creating ' + opts.requiredCopayers + ' of ' + opts.totalCopayers + ' wallet');
|
2014-04-16 15:45:22 -07:00
|
|
|
|
2014-04-16 13:50:10 -07:00
|
|
|
this.id = opts.id || Wallet.getRandomId();
|
2014-04-24 12:35:52 -07:00
|
|
|
this.name = opts.name;
|
2014-04-16 18:12:30 -07:00
|
|
|
this.verbose = opts.verbose;
|
2014-04-16 13:50:10 -07:00
|
|
|
this.publicKeyRing.walletId = this.id;
|
|
|
|
this.txProposals.walletId = this.id;
|
2014-04-16 18:12:30 -07:00
|
|
|
|
2014-04-24 19:13:55 -07:00
|
|
|
this.network.maxPeers = this.totalCopayers;
|
2014-04-14 13:42:10 -07:00
|
|
|
}
|
2014-04-10 13:57:41 -07:00
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
Wallet.parent = EventEmitter;
|
|
|
|
Wallet.prototype.log = function() {
|
2014-04-17 05:38:46 -07:00
|
|
|
if (!this.verbose) return;
|
2014-04-20 16:24:24 -07:00
|
|
|
if (console)
|
2014-04-24 16:56:36 -07:00
|
|
|
console.log.apply(console, arguments);
|
2014-04-16 13:50:10 -07:00
|
|
|
};
|
2014-04-15 10:28:49 -07:00
|
|
|
|
2014-04-16 13:50:10 -07:00
|
|
|
Wallet.getRandomId = function() {
|
2014-04-24 11:49:37 -07:00
|
|
|
var r = bitcore.SecureRandom.getPseudoRandomBuffer(8).toString('hex');
|
|
|
|
return r;
|
2014-04-16 13:50:10 -07:00
|
|
|
};
|
2014-04-15 10:28:49 -07:00
|
|
|
|
2014-04-16 13:50:10 -07:00
|
|
|
Wallet.prototype._handlePublicKeyRing = function(senderId, data, isInbound) {
|
2014-04-24 16:56:36 -07:00
|
|
|
this.log('RECV PUBLICKEYRING:', data);
|
2014-04-16 13:50:10 -07:00
|
|
|
|
|
|
|
var recipients, pkr = this.publicKeyRing;
|
|
|
|
var inPKR = copay.PublicKeyRing.fromObj(data.publicKeyRing);
|
2014-04-18 14:25:51 -07:00
|
|
|
|
|
|
|
var hasChanged = pkr.merge(inPKR, true);
|
2014-04-24 16:56:36 -07:00
|
|
|
if (hasChanged) {
|
2014-04-16 13:50:10 -07:00
|
|
|
this.log('### BROADCASTING PKR');
|
|
|
|
recipients = null;
|
|
|
|
this.sendPublicKeyRing(recipients);
|
2014-04-24 19:13:55 -07:00
|
|
|
if (this.publicKeyRing.isComplete()) {
|
|
|
|
this._lockIncomming();
|
|
|
|
}
|
2014-04-16 13:50:10 -07:00
|
|
|
}
|
2014-04-25 13:53:19 -07:00
|
|
|
this.emit('publicKeyRingUpdated', this.publicKeyRing);
|
2014-04-16 13:50:10 -07:00
|
|
|
this.store();
|
2014-04-14 13:42:10 -07:00
|
|
|
};
|
2014-04-14 13:17:56 -07:00
|
|
|
|
2014-04-14 14:30:08 -07:00
|
|
|
|
2014-04-16 13:50:10 -07:00
|
|
|
Wallet.prototype._handleTxProposals = function(senderId, data, isInbound) {
|
2014-04-24 19:13:55 -07:00
|
|
|
this.log('RECV TXPROPOSAL:', data);
|
2014-04-14 13:17:56 -07:00
|
|
|
|
2014-04-16 13:50:10 -07:00
|
|
|
var recipients;
|
|
|
|
var inTxp = copay.TxProposals.fromObj(data.txProposals);
|
|
|
|
var mergeInfo = this.txProposals.merge(inTxp, true);
|
|
|
|
var addSeen = this.addSeenToTxProposals();
|
2014-04-24 16:56:36 -07:00
|
|
|
if (mergeInfo.hasChanged || addSeen) {
|
|
|
|
this.log('### BROADCASTING txProposals. ');
|
2014-04-16 13:50:10 -07:00
|
|
|
recipients = null;
|
|
|
|
this.sendTxProposals(recipients);
|
2014-04-24 19:13:55 -07:00
|
|
|
}
|
2014-04-25 13:53:19 -07:00
|
|
|
this.emit('txProposalsUpdated', this.txProposals);
|
2014-04-16 13:50:10 -07:00
|
|
|
this.store();
|
2014-04-14 13:42:10 -07:00
|
|
|
};
|
|
|
|
|
2014-04-16 13:50:10 -07:00
|
|
|
Wallet.prototype._handleData = function(senderId, data, isInbound) {
|
2014-04-20 12:16:09 -07:00
|
|
|
// TODO check message signature
|
2014-04-17 12:27:15 -07:00
|
|
|
if (this.id !== data.walletId) {
|
2014-04-24 16:56:36 -07:00
|
|
|
this.emit('badMessage', senderId);
|
2014-04-17 12:27:15 -07:00
|
|
|
this.log('badMessage FROM:', senderId); //TODO
|
|
|
|
return;
|
|
|
|
}
|
2014-04-24 16:56:36 -07:00
|
|
|
this.log('[Wallet.js.98]', data.type); //TODO
|
|
|
|
switch (data.type) {
|
2014-04-20 13:16:21 -07:00
|
|
|
// This handler is repeaded on WalletFactory (#join). TODO
|
|
|
|
case 'walletId':
|
|
|
|
this.sendWalletReady(senderId);
|
|
|
|
break;
|
2014-04-20 12:16:09 -07:00
|
|
|
case 'walletReady':
|
|
|
|
this.sendPublicKeyRing(senderId);
|
|
|
|
this.sendTxProposals(senderId);
|
|
|
|
break;
|
2014-04-16 13:50:10 -07:00
|
|
|
case 'publicKeyRing':
|
|
|
|
this._handlePublicKeyRing(senderId, data, isInbound);
|
2014-04-24 16:56:36 -07:00
|
|
|
break;
|
2014-04-16 13:50:10 -07:00
|
|
|
case 'txProposals':
|
|
|
|
this._handleTxProposals(senderId, data, isInbound);
|
2014-04-24 16:56:36 -07:00
|
|
|
break;
|
2014-04-16 13:50:10 -07:00
|
|
|
}
|
|
|
|
};
|
2014-04-14 13:42:10 -07:00
|
|
|
|
2014-04-23 09:44:20 -07:00
|
|
|
Wallet.prototype._handleNetworkChange = function(newCopayerId) {
|
2014-04-23 12:02:23 -07:00
|
|
|
if (newCopayerId) {
|
2014-04-25 13:36:00 -07:00
|
|
|
this.log('#### Setting new COPAYER:', newCopayerId);
|
2014-04-23 09:44:20 -07:00
|
|
|
this.sendWalletId(newCopayerId);
|
2014-04-24 16:56:36 -07:00
|
|
|
this.emit('peer', this.network.peerFromCopayer(newCopayerId));
|
2014-04-17 12:27:15 -07:00
|
|
|
}
|
2014-04-17 07:46:49 -07:00
|
|
|
this.emit('refresh');
|
2014-04-14 13:42:10 -07:00
|
|
|
};
|
|
|
|
|
2014-04-20 12:16:09 -07:00
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
Wallet.prototype._optsToObj = function() {
|
2014-04-17 14:02:20 -07:00
|
|
|
var obj = {
|
|
|
|
id: this.id,
|
|
|
|
spendUnconfirmed: this.spendUnconfirmed,
|
|
|
|
requiredCopayers: this.requiredCopayers,
|
|
|
|
totalCopayers: this.totalCopayers,
|
2014-04-24 12:35:52 -07:00
|
|
|
name: this.name,
|
2014-04-17 14:02:20 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
};
|
|
|
|
|
2014-04-18 10:40:16 -07:00
|
|
|
|
2014-04-23 09:44:20 -07:00
|
|
|
Wallet.prototype.getCopayerId = function(index) {
|
2014-04-21 08:10:34 -07:00
|
|
|
return this.publicKeyRing.getCopayerId(index || 0);
|
2014-04-20 08:41:28 -07:00
|
|
|
};
|
|
|
|
|
2014-04-18 14:25:51 -07:00
|
|
|
|
2014-04-23 09:44:20 -07:00
|
|
|
Wallet.prototype.getMyCopayerId = function() {
|
|
|
|
return this.getCopayerId(0);
|
2014-04-18 10:40:16 -07:00
|
|
|
};
|
|
|
|
|
2014-04-24 19:13:55 -07:00
|
|
|
Wallet.prototype._lockIncomming = function() {
|
|
|
|
this.network.lockIncommingConnections(this.publicKeyRing.getAllCopayerIds());
|
|
|
|
};
|
|
|
|
|
2014-04-16 15:18:19 -07:00
|
|
|
Wallet.prototype.netStart = function() {
|
2014-04-16 13:50:10 -07:00
|
|
|
var self = this;
|
|
|
|
var net = this.network;
|
2014-04-17 12:50:48 -07:00
|
|
|
net.removeAllListeners();
|
2014-04-24 16:56:36 -07:00
|
|
|
net.on('networkChange', self._handleNetworkChange.bind(self));
|
|
|
|
net.on('data', self._handleData.bind(self));
|
|
|
|
net.on('open', function() {}); // TODO
|
2014-04-17 12:27:15 -07:00
|
|
|
net.on('openError', function() {
|
2014-04-18 14:25:51 -07:00
|
|
|
self.log('[Wallet.js.132:openError:] GOT openError'); //TODO
|
2014-04-17 12:27:15 -07:00
|
|
|
self.emit('openError');
|
|
|
|
});
|
|
|
|
net.on('close', function() {
|
|
|
|
self.emit('close');
|
|
|
|
});
|
2014-04-20 08:41:28 -07:00
|
|
|
|
2014-04-23 09:44:20 -07:00
|
|
|
var myId = self.getMyCopayerId();
|
2014-04-24 16:56:36 -07:00
|
|
|
var startOpts = {
|
2014-04-23 18:43:17 -07:00
|
|
|
copayerId: myId,
|
2014-04-24 19:13:55 -07:00
|
|
|
maxPeers: self.totalCopayers,
|
2014-04-20 08:41:28 -07:00
|
|
|
};
|
2014-04-24 12:35:52 -07:00
|
|
|
|
2014-04-24 19:13:55 -07:00
|
|
|
if (this.publicKeyRing.isComplete()) {
|
|
|
|
this._lockIncomming();
|
|
|
|
}
|
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
net.start(startOpts, function() {
|
2014-04-23 17:20:44 -07:00
|
|
|
self.emit('created', net.getPeer());
|
2014-04-24 12:07:49 -07:00
|
|
|
var registered = self.getRegisteredPeerIds();
|
2014-04-24 16:56:36 -07:00
|
|
|
for (var i = 0; i < self.publicKeyRing.registeredCopayers(); i++) {
|
|
|
|
var otherId = self.getCopayerId(i);
|
|
|
|
if (otherId !== myId) {
|
|
|
|
net.connectTo(otherId);
|
2014-04-18 14:25:51 -07:00
|
|
|
}
|
2014-04-24 16:56:36 -07:00
|
|
|
if (self.firstCopayerId) {
|
|
|
|
self.sendWalletReady(self.firstCopayerId);
|
|
|
|
self.firstCopayerId = null;
|
|
|
|
}
|
|
|
|
self.emit('refresh');
|
2014-04-18 14:25:51 -07:00
|
|
|
}
|
2014-04-23 18:43:17 -07:00
|
|
|
});
|
2014-04-16 13:50:10 -07:00
|
|
|
};
|
2014-04-15 06:22:50 -07:00
|
|
|
|
2014-04-24 12:07:49 -07:00
|
|
|
Wallet.prototype.getOnlinePeerIDs = function() {
|
|
|
|
return this.network.getOnlinePeerIDs();
|
|
|
|
};
|
|
|
|
|
|
|
|
Wallet.prototype.getRegisteredPeerIds = function() {
|
|
|
|
var ret = [];
|
2014-04-24 16:56:36 -07:00
|
|
|
for (var i = 0; i < this.publicKeyRing.registeredCopayers(); i++) {
|
|
|
|
var cid = this.getCopayerId(i)
|
|
|
|
var pid = this.network.peerFromCopayer(cid);
|
|
|
|
ret.push(pid);
|
2014-04-24 12:07:49 -07:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2014-04-18 09:20:35 -07:00
|
|
|
Wallet.prototype.store = function(isSync) {
|
2014-04-17 14:02:20 -07:00
|
|
|
var wallet = this.toObj();
|
|
|
|
this.storage.setFromObj(this.id, wallet);
|
2014-04-16 18:12:30 -07:00
|
|
|
|
2014-04-18 09:20:35 -07:00
|
|
|
if (isSync) {
|
|
|
|
this.log('Wallet stored.'); //TODO
|
2014-04-24 16:56:36 -07:00
|
|
|
} else {
|
2014-04-18 09:20:35 -07:00
|
|
|
this.log('Wallet stored. REFRESH Emitted'); //TODO
|
|
|
|
this.emit('refresh');
|
|
|
|
}
|
2014-04-17 14:02:20 -07:00
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
Wallet.prototype.toObj = function() {
|
|
|
|
var optsObj = this._optsToObj();
|
|
|
|
var walletObj = {
|
|
|
|
opts: optsObj,
|
2014-04-17 14:55:17 -07:00
|
|
|
publicKeyRing: this.publicKeyRing.toObj(),
|
2014-04-17 14:02:20 -07:00
|
|
|
txProposals: this.txProposals.toObj(),
|
|
|
|
privateKey: this.privateKey.toObj()
|
|
|
|
};
|
|
|
|
|
|
|
|
return walletObj;
|
2014-04-15 06:22:50 -07:00
|
|
|
};
|
|
|
|
|
2014-04-17 14:02:20 -07:00
|
|
|
Wallet.fromObj = function(wallet) {
|
|
|
|
var opts = wallet.opts;
|
|
|
|
opts['publicKeyRing'] = this.publicKeyring.fromObj(wallet.publicKeyRing);
|
|
|
|
opts['txProposals'] = this.txProposal.fromObj(wallet.txProposals);
|
|
|
|
opts['privateKey'] = this.privateKey.fromObj(wallet.privateKey);
|
|
|
|
|
|
|
|
var w = new Wallet(opts);
|
|
|
|
|
|
|
|
return w;
|
|
|
|
};
|
2014-04-15 08:17:28 -07:00
|
|
|
|
|
|
|
Wallet.prototype.sendTxProposals = function(recipients) {
|
2014-04-24 16:56:36 -07:00
|
|
|
this.log('### SENDING txProposals TO:', recipients || 'All', this.txProposals);
|
2014-04-15 08:17:28 -07:00
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
this.network.send(recipients, {
|
|
|
|
type: 'txProposals',
|
2014-04-15 08:17:28 -07:00
|
|
|
txProposals: this.txProposals.toObj(),
|
|
|
|
walletId: this.id,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-04-20 12:16:09 -07:00
|
|
|
Wallet.prototype.sendWalletReady = function(recipients) {
|
|
|
|
this.log('### SENDING WalletReady TO:', recipients);
|
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
this.network.send(recipients, {
|
|
|
|
type: 'walletReady',
|
2014-04-20 12:16:09 -07:00
|
|
|
walletId: this.id,
|
|
|
|
});
|
|
|
|
this.emit('walletReady');
|
|
|
|
};
|
2014-04-16 16:58:57 -07:00
|
|
|
|
|
|
|
Wallet.prototype.sendWalletId = function(recipients) {
|
2014-04-25 13:36:00 -07:00
|
|
|
this.log('### SENDING walletId TO:', recipients || 'All', this.id);
|
2014-04-16 16:58:57 -07:00
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
this.network.send(recipients, {
|
|
|
|
type: 'walletId',
|
2014-04-16 16:58:57 -07:00
|
|
|
walletId: this.id,
|
2014-04-18 14:25:51 -07:00
|
|
|
opts: this._optsToObj()
|
2014-04-16 16:58:57 -07:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2014-04-15 08:17:28 -07:00
|
|
|
Wallet.prototype.sendPublicKeyRing = function(recipients) {
|
2014-04-24 16:56:36 -07:00
|
|
|
this.log('### SENDING publicKeyRing TO:', recipients || 'All', this.publicKeyRing.toObj());
|
2014-04-15 08:17:28 -07:00
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
this.network.send(recipients, {
|
|
|
|
type: 'publicKeyRing',
|
2014-04-15 08:17:28 -07:00
|
|
|
publicKeyRing: this.publicKeyRing.toObj(),
|
|
|
|
walletId: this.id,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-04-17 12:27:15 -07:00
|
|
|
|
2014-04-21 07:28:25 -07:00
|
|
|
Wallet.prototype.generateAddress = function(isChange) {
|
|
|
|
var addr = this.publicKeyRing.generateAddress(isChange);
|
2014-04-16 13:50:10 -07:00
|
|
|
this.sendPublicKeyRing();
|
2014-04-18 09:20:35 -07:00
|
|
|
this.store(true);
|
2014-04-15 12:50:16 -07:00
|
|
|
return addr;
|
|
|
|
};
|
2014-04-15 08:17:28 -07:00
|
|
|
|
2014-04-22 22:01:54 -07:00
|
|
|
|
2014-04-15 11:25:55 -07:00
|
|
|
Wallet.prototype.getTxProposals = function() {
|
|
|
|
var ret = [];
|
2014-04-24 16:56:36 -07:00
|
|
|
for (var k in this.txProposals.txps) {
|
2014-04-22 22:01:54 -07:00
|
|
|
var i = this.txProposals.getTxProposal(k);
|
2014-04-24 16:56:36 -07:00
|
|
|
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;
|
2014-04-21 03:27:45 -07:00
|
|
|
|
2014-04-15 11:25:55 -07:00
|
|
|
ret.push(i);
|
2014-04-16 13:50:10 -07:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2014-04-22 22:01:54 -07:00
|
|
|
|
|
|
|
Wallet.prototype.reject = function(ntxid) {
|
2014-04-24 16:56:36 -07:00
|
|
|
var myId = this.getMyCopayerId();
|
2014-04-18 15:28:28 -07:00
|
|
|
var txp = this.txProposals.txps[ntxid];
|
2014-04-22 22:01:54 -07:00
|
|
|
if (!txp || txp.rejectedBy[myId] || txp.signedBy[myId]) return;
|
|
|
|
|
|
|
|
txp.rejectedBy[myId] = Date.now();
|
|
|
|
this.sendTxProposals();
|
|
|
|
this.store(true);
|
2014-04-18 15:28:28 -07:00
|
|
|
};
|
2014-04-16 13:50:10 -07:00
|
|
|
|
2014-04-22 22:01:54 -07:00
|
|
|
|
2014-04-16 13:50:10 -07:00
|
|
|
Wallet.prototype.sign = function(ntxid) {
|
2014-04-18 15:28:28 -07:00
|
|
|
var self = this;
|
2014-04-24 16:56:36 -07:00
|
|
|
var myId = this.getMyCopayerId();
|
2014-04-18 15:28:28 -07:00
|
|
|
var txp = self.txProposals.txps[ntxid];
|
2014-04-22 22:01:54 -07:00
|
|
|
if (!txp || txp.rejectedBy[myId] || txp.signedBy[myId]) return;
|
2014-04-16 13:50:10 -07:00
|
|
|
|
2014-04-18 15:28:28 -07:00
|
|
|
var pkr = self.publicKeyRing;
|
|
|
|
var keys = self.privateKey.getAll(pkr.addressIndex, pkr.changeAddressIndex);
|
|
|
|
|
|
|
|
var b = txp.builder;
|
|
|
|
var before = b.signaturesAdded;
|
|
|
|
b.sign(keys);
|
|
|
|
|
|
|
|
var ret = false;
|
2014-04-24 16:56:36 -07:00
|
|
|
if (b.signaturesAdded > before) {
|
2014-04-22 22:01:54 -07:00
|
|
|
txp.signedBy[myId] = Date.now();
|
2014-04-18 15:28:28 -07:00
|
|
|
this.sendTxProposals();
|
|
|
|
this.store(true);
|
|
|
|
ret = true;
|
2014-04-16 13:50:10 -07:00
|
|
|
}
|
2014-04-15 11:25:55 -07:00
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2014-04-20 17:53:54 -07:00
|
|
|
Wallet.prototype.sendTx = function(ntxid, cb) {
|
2014-04-18 15:28:28 -07:00
|
|
|
var txp = this.txProposals.txps[ntxid];
|
|
|
|
if (!txp) return;
|
|
|
|
|
|
|
|
var tx = txp.builder.build();
|
|
|
|
if (!tx.isComplete()) return;
|
|
|
|
this.log('[Wallet.js.231] BROADCASTING TX!!!'); //TODO
|
|
|
|
|
|
|
|
var txHex = tx.serialize().toString('hex');
|
2014-04-24 16:56:36 -07:00
|
|
|
this.log('[Wallet.js.261:txHex:]', txHex); //TODO
|
2014-04-18 15:28:28 -07:00
|
|
|
|
|
|
|
var self = this;
|
2014-04-21 07:28:25 -07:00
|
|
|
|
2014-04-18 15:28:28 -07:00
|
|
|
this.blockchain.sendRawTransaction(txHex, function(txid) {
|
2014-04-24 16:56:36 -07:00
|
|
|
self.log('BITCOND txid:', txid); //TODO
|
2014-04-18 15:28:28 -07:00
|
|
|
if (txid) {
|
2014-04-21 03:30:46 -07:00
|
|
|
self.txProposals.setSent(ntxid, txid);
|
2014-04-18 15:28:28 -07:00
|
|
|
}
|
2014-04-20 17:53:54 -07:00
|
|
|
self.sendTxProposals();
|
|
|
|
self.store();
|
|
|
|
return cb(txid);
|
2014-04-18 15:28:28 -07:00
|
|
|
});
|
|
|
|
};
|
2014-04-15 11:25:55 -07:00
|
|
|
|
|
|
|
Wallet.prototype.addSeenToTxProposals = function() {
|
2014-04-24 16:56:36 -07:00
|
|
|
var ret = false;
|
|
|
|
var myId = this.getMyCopayerId();
|
2014-04-16 13:50:10 -07:00
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
for (var k in this.txProposals.txps) {
|
2014-04-18 15:28:28 -07:00
|
|
|
var txp = this.txProposals.txps[k];
|
2014-04-20 16:24:24 -07:00
|
|
|
if (!txp.seenBy[myId]) {
|
|
|
|
|
|
|
|
txp.seenBy[myId] = Date.now();
|
2014-04-15 11:25:55 -07:00
|
|
|
ret = true;
|
|
|
|
}
|
2014-04-18 15:28:28 -07:00
|
|
|
}
|
2014-04-15 11:25:55 -07:00
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2014-04-17 13:22:50 -07:00
|
|
|
Wallet.prototype.getAddresses = function(onlyMain) {
|
|
|
|
return this.publicKeyRing.getAddresses(onlyMain);
|
2014-04-15 11:50:22 -07:00
|
|
|
};
|
|
|
|
|
2014-04-17 13:22:50 -07:00
|
|
|
Wallet.prototype.getAddressesStr = function(onlyMain) {
|
2014-04-15 14:23:35 -07:00
|
|
|
var ret = [];
|
2014-04-17 13:22:50 -07:00
|
|
|
this.publicKeyRing.getAddresses(onlyMain).forEach(function(a) {
|
2014-04-15 14:23:35 -07:00
|
|
|
ret.push(a.toString());
|
|
|
|
});
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2014-04-18 07:19:39 -07:00
|
|
|
Wallet.prototype.addressIsOwn = function(addrStr) {
|
|
|
|
var addrList = this.getAddressesStr();
|
|
|
|
var l = addrList.length;
|
|
|
|
var ret = false;
|
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
for (var i = 0; i < l; i++) {
|
2014-04-18 07:19:39 -07:00
|
|
|
if (addrList[i] === addrStr) {
|
2014-04-24 16:56:36 -07:00
|
|
|
ret = true;
|
2014-04-18 07:19:39 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2014-04-21 08:37:32 -07:00
|
|
|
Wallet.prototype.getBalance = function(safe, cb) {
|
2014-04-18 09:20:35 -07:00
|
|
|
var balance = 0;
|
|
|
|
var balanceByAddr = {};
|
2014-04-21 07:28:25 -07:00
|
|
|
var isMain = {};
|
2014-04-18 09:20:35 -07:00
|
|
|
var COIN = bitcore.util.COIN;
|
2014-04-18 15:28:28 -07:00
|
|
|
var addresses = this.getAddressesStr(true);
|
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
if (!addresses.length) return cb(0, []);
|
2014-04-18 07:19:39 -07:00
|
|
|
|
2014-04-18 09:20:35 -07:00
|
|
|
// Prefill balanceByAddr with main address
|
2014-04-24 16:56:36 -07:00
|
|
|
addresses.forEach(function(a) {
|
|
|
|
balanceByAddr[a] = 0;
|
|
|
|
isMain[a] = 1;
|
2014-04-17 12:42:27 -07:00
|
|
|
});
|
2014-04-24 16:56:36 -07:00
|
|
|
var f = safe ? this.getSafeUnspent.bind(this) : this.getUnspent.bind(this);
|
2014-04-21 08:37:32 -07:00
|
|
|
f(function(utxos) {
|
2014-04-24 16:56:36 -07:00
|
|
|
for (var i = 0; i < utxos.length; i++) {
|
|
|
|
var u = utxos[i];
|
2014-04-18 09:20:35 -07:00
|
|
|
var amt = u.amount * COIN;
|
|
|
|
balance = balance + amt;
|
2014-04-24 16:56:36 -07:00
|
|
|
balanceByAddr[u.address] = (balanceByAddr[u.address] || 0) + amt;
|
2014-04-17 07:51:56 -07:00
|
|
|
}
|
2014-04-24 16:56:36 -07:00
|
|
|
for (var a in balanceByAddr) {
|
|
|
|
balanceByAddr[a] = balanceByAddr[a] / COIN;
|
2014-04-21 08:00:14 -07:00
|
|
|
}
|
2014-04-21 07:28:25 -07:00
|
|
|
return cb(balance / COIN, balanceByAddr, isMain);
|
2014-04-17 02:43:30 -07:00
|
|
|
});
|
|
|
|
};
|
2014-04-15 14:23:35 -07:00
|
|
|
|
2014-04-18 09:20:35 -07:00
|
|
|
Wallet.prototype.getUnspent = function(cb) {
|
|
|
|
this.blockchain.getUnspent(this.getAddressesStr(), function(unspentList) {
|
|
|
|
return cb(unspentList);
|
2014-04-17 12:42:27 -07:00
|
|
|
});
|
2014-04-15 14:23:35 -07:00
|
|
|
};
|
|
|
|
|
2014-04-21 08:37:32 -07:00
|
|
|
Wallet.prototype.getSafeUnspent = function(cb) {
|
|
|
|
var self = this;
|
|
|
|
this.blockchain.getUnspent(this.getAddressesStr(), function(unspentList) {
|
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
var ret = [];
|
2014-04-22 22:01:54 -07:00
|
|
|
var maxRejectCount = self.totalCopayers - self.requiredCopayers;
|
|
|
|
var uu = self.txProposals.getUsedUnspent(maxRejectCount);
|
2014-04-21 08:37:32 -07:00
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
for (var i in unspentList) {
|
|
|
|
if (uu.indexOf(unspentList[i].txid) === -1)
|
2014-04-21 08:37:32 -07:00
|
|
|
ret.push(unspentList[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cb(ret);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2014-04-16 13:50:10 -07:00
|
|
|
Wallet.prototype.createTx = function(toAddress, amountSatStr, opts, cb) {
|
|
|
|
var self = this;
|
|
|
|
if (typeof opts === 'function') {
|
|
|
|
cb = opts;
|
|
|
|
opts = {};
|
|
|
|
}
|
|
|
|
opts = opts || {};
|
|
|
|
|
|
|
|
if (typeof opts.spendUnconfirmed === 'undefined') {
|
|
|
|
opts.spendUnconfirmed = this.spendUnconfirmed;
|
|
|
|
}
|
2014-04-21 08:37:32 -07:00
|
|
|
|
|
|
|
self.getSafeUnspent(function(unspentList) {
|
2014-04-22 18:07:18 -07:00
|
|
|
if (self.createTxSync(toAddress, amountSatStr, unspentList, opts)) {
|
2014-04-24 16:56:36 -07:00
|
|
|
self.sendPublicKeyRing(); // Change Address
|
2014-04-22 18:07:18 -07:00
|
|
|
self.sendTxProposals();
|
|
|
|
self.store();
|
|
|
|
}
|
2014-04-16 13:50:10 -07:00
|
|
|
return cb();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
Wallet.prototype.createTxSync = function(toAddress, amountSatStr, utxos, opts) {
|
2014-04-24 16:56:36 -07:00
|
|
|
var pkr = this.publicKeyRing;
|
2014-04-15 14:23:35 -07:00
|
|
|
var priv = this.privateKey;
|
|
|
|
opts = opts || {};
|
|
|
|
|
|
|
|
var amountSat = bitcore.bignum(amountSatStr);
|
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
if (!pkr.isComplete()) {
|
2014-04-15 14:23:35 -07:00
|
|
|
throw new Error('publicKeyRing is not complete');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!opts.remainderOut) {
|
2014-04-24 16:56:36 -07:00
|
|
|
opts.remainderOut = {
|
|
|
|
address: this.generateAddress(true).toString()
|
|
|
|
};
|
2014-04-21 07:28:25 -07:00
|
|
|
}
|
2014-04-15 14:23:35 -07:00
|
|
|
|
|
|
|
var b = new Builder(opts)
|
|
|
|
.setUnspent(utxos)
|
|
|
|
.setHashToScriptMap(pkr.getRedeemScriptMap())
|
2014-04-24 16:56:36 -07:00
|
|
|
.setOutputs([{
|
|
|
|
address: toAddress,
|
|
|
|
amountSat: amountSat
|
|
|
|
}]);
|
2014-04-15 14:23:35 -07:00
|
|
|
|
2014-04-24 16:56:36 -07:00
|
|
|
var signRet;
|
2014-04-15 14:23:35 -07:00
|
|
|
if (priv) {
|
2014-04-24 16:56:36 -07:00
|
|
|
b.sign(priv.getAll(pkr.addressIndex, pkr.changeAddressIndex));
|
2014-04-15 14:23:35 -07:00
|
|
|
}
|
2014-04-23 09:44:20 -07:00
|
|
|
var myId = this.getMyCopayerId();
|
2014-04-20 16:24:24 -07:00
|
|
|
var now = Date.now();
|
|
|
|
|
2014-04-22 22:01:54 -07:00
|
|
|
var me = {};
|
|
|
|
if (priv && b.signaturesAdded) me[myId] = now;
|
|
|
|
|
|
|
|
var meSeen = {};
|
|
|
|
if (priv) meSeen[myId] = now;
|
2014-04-20 16:24:24 -07:00
|
|
|
|
|
|
|
var data = {
|
2014-04-22 22:01:54 -07:00
|
|
|
signedBy: me,
|
2014-04-24 16:56:36 -07:00
|
|
|
seenBy: meSeen,
|
|
|
|
creator: myId,
|
2014-04-20 16:24:24 -07:00
|
|
|
createdTs: now,
|
2014-04-15 14:23:35 -07:00
|
|
|
builder: b,
|
2014-04-20 16:24:24 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
this.txProposals.add(data);
|
2014-04-22 22:01:54 -07:00
|
|
|
return true;
|
2014-04-15 14:23:35 -07:00
|
|
|
};
|
|
|
|
|
2014-04-16 13:50:10 -07:00
|
|
|
Wallet.prototype.connectTo = function(peerId) {
|
2014-04-16 16:58:57 -07:00
|
|
|
throw new Error('Wallet.connectTo.. not yet implemented!');
|
2014-04-15 11:50:22 -07:00
|
|
|
};
|
2014-04-16 16:58:57 -07:00
|
|
|
|
|
|
|
Wallet.prototype.disconnect = function() {
|
2014-04-24 19:13:55 -07:00
|
|
|
this.log('## DISCONNECTING');
|
2014-04-16 16:58:57 -07:00
|
|
|
this.network.disconnect();
|
|
|
|
};
|
|
|
|
|
2014-04-23 17:20:44 -07:00
|
|
|
Wallet.prototype.getNetwork = function() {
|
|
|
|
return this.network;
|
|
|
|
};
|
|
|
|
|
2014-04-14 14:30:08 -07:00
|
|
|
module.exports = require('soop')(Wallet);
|