'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]; }); console.log('creating '+opts.requiredCopayers+' of '+opts.totalCopayers+' wallet'); this.id = opts.id || Wallet.getRandomId(); 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); if (pkr.merge(inPKR, true) && !data.isBroadcast) { 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) { 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) { if (this.id !== data.walletId) throw new Error('wrong message received: Bad wallet ID'); switch(data.type) { case 'publicKeyRing': this._handlePublicKeyRing(senderId, data, isInbound); break; case 'txProposals': this._handleTxProposals(senderId, data, isInbound); break; case 'abort': this.emit('abort'); break; } }; Wallet.prototype._handleNetworkChange = function(newPeer) { console.log('[Wallet.js.112:newPeer:]',newPeer); //TODO if (!newPeer) return; console.log('[Wallet.js.112:newPeer:]',newPeer); //TODO this.log('#### Setting new PEER:', newPeer); this.sendWalletId(newPeer); this.sendPublicKeyRing(newPeer); this.sendTxProposals(newPeer); }; Wallet.prototype.netStart = function() { var self = this; var net = this.network; net.on('networkChange', self._handleNetworkChange.bind(self) ); net.on('data', self._handleData.bind(self) ); net.on('open', function() {}); // TODO net.on('close', function() {}); // TODO net.start(function(peerId) { self.emit('created'); }); }; Wallet.prototype.store = function() { this.storage.set(this.id,'opts', { id: this.id, spendUnconfirmed: this.spendUnconfirmed, requiredCopayers: this.requiredCopayers, totalCopayers: this.totalCopayers, }); this.storage.set(this.id,'publicKeyRing', this.publicKeyRing.toObj()); this.storage.set(this.id,'txProposals', this.txProposals.toObj()); this.storage.set(this.id,'privateKey', this.privateKey.toObj()); }; Wallet.prototype.sendTxProposals = function(recipients) { this.log('### SENDING txProposals TO:', recipients||'All', this.txProposals); this.network.send( recipients, { type: 'txProposals', txProposals: this.txProposals.toObj(), walletId: this.id, }); this.emit('txProposalsUpdated', this.txProposals); }; Wallet.prototype.sendWalletId = function(recipients) { this.log('### SENDING walletId TO:', recipients||'All', this.walletId); this.network.send(recipients, { type: 'walletId', walletId: this.id, }); }; Wallet.prototype.sendPublicKeyRing = function(recipients) { this.log('### SENDING publicKeyRing TO:', recipients||'All', this.publicKeyRing.toObj()); this.network.send(recipients, { type: 'publicKeyRing', publicKeyRing: this.publicKeyRing.toObj(), walletId: this.id, }); this.emit('publicKeyRingUpdated', this.publicKeyRing); }; Wallet.prototype.generateAddress = function() { var addr = this.publicKeyRing.generateAddress(); this.store(); this.sendPublicKeyRing(); return addr; }; Wallet.prototype.getTxProposals = function() { var ret = []; var self= this; self.txProposals.txps.forEach(function(txp) { var i = {txp:txp}; i.ntxid = txp.builder.build().getNormalizedHash(); i.signedByUs = txp.signedBy[self.privateKey.id]?true:false; ret.push(i); }); return ret; }; // TODO: this can be precalculated. Wallet.prototype._findTxByNtxid = function(ntxid) { var ret; var l = this.txProposals.txps.length; var id = ntxid.toString('hex'); for(var i=0; i