'use strict'; var imports = require('soop').imports(); var bitcore = require('bitcore'); 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'); function Wallet(opts) { var self = this; //required params ['storage', 'network', 'blockchain', 'requiredCopayers', 'totalCopayers', 'spendUnconfirmed', 'publicKeyRing', 'txProposals', 'privateKey' ].forEach( function(k){ if (typeof opts[k] === 'undefined') throw new Error('missing key:' + k); self[k] = opts[k]; }); this.log('creating '+opts.requiredCopayers+' of '+opts.totalCopayers+' wallet'); this.id = opts.id || Wallet.getRandomId(); this.verbose = opts.verbose; this.publicKeyRing.walletId = this.id; this.txProposals.walletId = this.id; } Wallet.parent=EventEmitter; Wallet.prototype.log = function(){ if (!this.verbose) return; console.log(arguments); }; Wallet.getRandomId = function() { var r = buffertools.toHex(coinUtil.generateNonce()); return r; }; Wallet.prototype._handlePublicKeyRing = function(senderId, data, isInbound) { this.log('RECV PUBLICKEYRING:',data); var shouldSend = false; var recipients, pkr = this.publicKeyRing; var inPKR = copay.PublicKeyRing.fromObj(data.publicKeyRing); var hasChanged = pkr.merge(inPKR, true); if (hasChanged) { this.log('### BROADCASTING PKR'); recipients = null; shouldSend = true; } // else if (isInbound && !data.isBroadcast) { // // always replying to connecting peer // this.log('### REPLYING PKR TO:', senderId); // recipients = senderId; // shouldSend = true; // } if (shouldSend) { this.sendPublicKeyRing(recipients); } this.store(); }; Wallet.prototype._handleTxProposals = function(senderId, data, isInbound) { this.log('RECV TXPROPOSAL:',data); //TODO var shouldSend = false; var recipients; var inTxp = copay.TxProposals.fromObj(data.txProposals); var mergeInfo = this.txProposals.merge(inTxp, true); var addSeen = this.addSeenToTxProposals(); // if ((mergeInfo.merged && !data.isBroadcast) || addSeen) { if (mergeInfo.merged || addSeen) { this.log('### BROADCASTING txProposals. ' ); recipients = null; shouldSend = true; } // else if (isInbound && !data.isBroadcast) { // // always replying to connecting peer // this.log('### REPLYING txProposals TO:', senderId); // recipients = senderId; // shouldSend = true; // } if (shouldSend) this.sendTxProposals(recipients); this.store(); }; Wallet.prototype._handleData = function(senderId, data, isInbound) { // TODO check message signature if (this.id !== data.walletId) { this.emit('badMessage',senderId); this.log('badMessage FROM:', senderId); //TODO return; } this.log('[Wallet.js.98]' , data.type); //TODO switch(data.type) { case 'walletReady': console.log('[Wallet.js.109] RECV WALLETREADY'); //TODO this.sendPublicKeyRing(senderId); this.sendTxProposals(senderId); break; case 'publicKeyRing': this._handlePublicKeyRing(senderId, data, isInbound); break; case 'txProposals': this._handleTxProposals(senderId, data, isInbound); break; } }; Wallet.prototype._handleNetworkChange = function(newPeerId) { if (newPeerId) { this.log('#### Setting new PEER:', newPeerId); this.sendWalletId(newPeerId); } this.emit('refresh'); }; Wallet.prototype._optsToObj = function () { var obj = { id: this.id, spendUnconfirmed: this.spendUnconfirmed, requiredCopayers: this.requiredCopayers, totalCopayers: this.totalCopayers, }; return obj; }; Wallet.prototype.getPeerId = function(index) { // if (typeof index === 'undefined') { // // return my own peerId // var gen = this.privateKey.getId(idBuf); // return gen; // } // return peer number 'index' peerId // var idBuf; // TODO idBuf DISABLED FOR NOW //idBuf = new Buffer(this.id); return this.publicKeyRing.getCopayerId(index || 0, idBuf); }; Wallet.prototype.getMyPeerId = function() { return this.getPeerId(0); }; Wallet.prototype.netStart = function() { console.log('[Wallet.js.159:netStart:]'); //TODO var self = this; var net = this.network; net.removeAllListeners(); net.on('networkChange', self._handleNetworkChange.bind(self) ); net.on('data', self._handleData.bind(self) ); net.on('open', function() {}); // TODO net.on('openError', function() { self.log('[Wallet.js.132:openError:] GOT openError'); //TODO self.emit('openError'); }); net.on('close', function() { self.emit('close'); }); var myPeerId = self.getMyPeerId(); var startOpts = { peerId: myPeerId }; net.start(function() { console.log('[Wallet.js.177] NET START: emit CREATED'); //TODO self.emit('created'); for (var i=0; i before) { txp.signedBy[self.privateKey.getId()] = Date.now(); this.sendTxProposals(); this.store(true); ret = true; } return ret; }; Wallet.prototype.sendTx = function(ntxid) { 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'); this.log('[Wallet.js.261:txHex:]',txHex); //TODO var self = this; this.blockchain.sendRawTransaction(txHex, function(txid) { self.log('BITCOND txid:',txid); //TODO if (txid) { self.txProposals.remove(ntxid); self.store(true); } return (txid); }); }; Wallet.prototype.addSeenToTxProposals = function() { var ret=false; var self=this; for(var k in this.txProposals.txps) { var txp = this.txProposals.txps[k]; if (!txp.seenBy[self.privateKey.getId()]) { txp.seenBy[self.privateKey.getId()] = Date.now(); ret = true; } } return ret; }; Wallet.prototype.getAddresses = function(onlyMain) { return this.publicKeyRing.getAddresses(onlyMain); }; Wallet.prototype.getAddressesStr = function(onlyMain) { var ret = []; this.publicKeyRing.getAddresses(onlyMain).forEach(function(a) { ret.push(a.toString()); }); return ret; }; Wallet.prototype.addressIsOwn = function(addrStr) { var addrList = this.getAddressesStr(); var l = addrList.length; var ret = false; for(var i=0; i