diff --git a/API.js b/API.js
index dd6d2fe19..76f30a29e 100644
--- a/API.js
+++ b/API.js
@@ -10,23 +10,13 @@ API.prototype._init = function(opts) {
opts = opts || {};
self.opts = opts;
- var Wallet = require('soop').load('./js/models/core/Wallet', {
+ var WalletFactory = require('soop').load('./js/models/core/WalletFactory', {
Storage: opts.Storage || require('./test/mocks/FakeStorage'),
- Network: opts.Network || require('./js/models/network/WebRTC'),
+ Network: opts.Network || require('./js/models/network/Base'),
Blockchain: opts.Blockchain || require('./js/models/blockchain/Insight')
});
-
- var config = {
- wallet: {
- requiredCopayers: opts.requiredCopayers || 3,
- totalCopayers: opts.totalCopayers || 5,
- }
- };
- var walletConfig = opts.walletConfig || config;
- var walletOpts = opts.walletOpts || {};
-
- self.wallet = self.opts.wallet || Wallet.factory.create(walletConfig, walletOpts);
+ this.walletFactory = new WalletFactory(opts);
};
API._coerceArgTypes = function(args, argTypes) {
@@ -179,13 +169,13 @@ API.prototype.getCommands = decorate('getCommands', [
['callback', 'function']
]);
-API.prototype._cmd_getPublicKeyRingId = function(callback) {
+API.prototype._cmd_getWalletIds = function(callback) {
var self = this;
- return callback(null, self.wallet.publicKeyRing.walletId);
+ return callback(null, self.walletFactory.getWalletIds());
};
-API.prototype.getPublicKeyRingId = decorate('getPublicKeyRingId', [
+API.prototype.getWalletIds = decorate('getWalletIds', [
['callback', 'function']
]);
diff --git a/bin/copay b/bin/copay
index 2721da0e2..9890f66cd 100755
--- a/bin/copay
+++ b/bin/copay
@@ -18,11 +18,10 @@ var main = function() {
var api = new API(commander);
var args = commander.args;
+ var command = args[0];
+ var commandArgs = args.slice(1);
try {
- var command = args[0];
- var commandArgs = args.slice(1);
-
if (command[0] == '_' || typeof api[command] != 'function')
throw new Error('invalid command');
diff --git a/copay.js b/copay.js
index fa34b585b..d68d1cfb1 100644
--- a/copay.js
+++ b/copay.js
@@ -10,11 +10,12 @@ var Insight = module.exports.Insight = require('./js/models/blockchain/Insight')
var StorageLocalPlain = module.exports.StorageLocalPlain = require('./js/models/storage/LocalPlain');
var StorageLocalEncrypted = module.exports.StorageLocalEncrypted = require('./js/models/storage/LocalEncrypted');
-module.exports.Wallet = require('soop').load('./js/models/core/Wallet',{
+var WalletFactory = require('soop').load('./js/models/core/WalletFactory',{
Network: WebRTC,
Blockchain: Insight,
Storage: StorageLocalPlain,
});
+module.exports.WalletFactory = WalletFactory;
module.exports.API = require('./API');
diff --git a/css/main.css b/css/main.css
index 9db1e305c..c42153d1b 100644
--- a/css/main.css
+++ b/css/main.css
@@ -135,7 +135,6 @@ h3 {
span.panel-res {
float: right;
- width: 3%;
padding: 0.4rem 0.55rem;
margin: 0 1rem;
border-radius: 1rem;
@@ -160,6 +159,11 @@ span.panel-res {
margin: 0.5rem 0 1rem;
}
+.share-wallet.panel {
+ background-color: #111;
+ color: #FBE500;
+}
+
button.primary { background-color: #111; }
button.secondary { background-color: #FAE448 !important; }
@@ -180,3 +184,8 @@ button.secondary:hover { background-color: #FFDF00 !important;}
.size-60 { font-size: 60px; }
.size-72 { font-size: 72px; }
.m10t {margin-top: 10px;}
+.p0r {padding-right: 0;}
+.p70r {padding-right: 70px;}
+.p70l {padding-left: 70px;}
+.p5h {padding: 0 5px;}
+.br100 {border-radius: 100%;}
\ No newline at end of file
diff --git a/index.html b/index.html
index f1b6c95cb..1b8f93c30 100644
--- a/index.html
+++ b/index.html
@@ -96,12 +96,53 @@
+
+
-
+
@@ -295,6 +342,7 @@
+
diff --git a/js/app.js b/js/app.js
index 56fc11cde..f1fff46a0 100644
--- a/js/app.js
+++ b/js/app.js
@@ -9,8 +9,9 @@ angular.module('copay',[
'copay.transactions',
'copay.send',
'copay.backup',
- 'copay.network',
+ 'copay.walletFactory',
'copay.signin',
+ 'copay.setup',
'copay.peer'
]);
@@ -19,7 +20,8 @@ angular.module('copay.home', []);
angular.module('copay.transactions', []);
angular.module('copay.send', []);
angular.module('copay.backup', []);
-angular.module('copay.network', []);
+angular.module('copay.walletFactory', []);
angular.module('copay.signin', []);
+angular.module('copay.setup', []);
angular.module('copay.peer', []);
diff --git a/js/config.js b/js/config.js
index 8e0d35ee8..d73f23cdb 100644
--- a/js/config.js
+++ b/js/config.js
@@ -7,9 +7,15 @@ var config = {
maxPeers: 3,
debug: 3,
},
+ limits: {
+ totalCopayers: 10,
+ mPlusN: 15
+ },
wallet: {
requiredCopayers: 2,
totalCopayers: 3,
+ spendUnconfirmed: 1,
+ verbose: 1,
},
blockchain: {
host: 'test.insight.is',
diff --git a/js/controllers/header.js b/js/controllers/header.js
index 9653731f9..91f00d863 100644
--- a/js/controllers/header.js
+++ b/js/controllers/header.js
@@ -1,7 +1,7 @@
'use strict';
angular.module('copay.header').controller('HeaderController',
- function($scope, $rootScope, $location, Network) {
+ function($scope, $rootScope, $location, walletFactory) {
$scope.menu = [{
'title': 'Home',
'icon': 'fi-home',
@@ -35,15 +35,13 @@ angular.module('copay.header').controller('HeaderController',
return false;
};
- $scope.init = function() {
- $rootScope.isLogged = false;
- };
-
$scope.signout = function() {
- Network.disconnect(function() {
+ var w = $rootScope.wallet;
+ if (w) {
+ w.disconnect();
+ delete $rootScope['wallet'];
$location.path('signin');
- $rootScope.$digest();
- });
+ }
};
$scope.clearFlashMessage = function() {
diff --git a/js/controllers/peer.js b/js/controllers/peer.js
index b99729a5a..f6f482315 100644
--- a/js/controllers/peer.js
+++ b/js/controllers/peer.js
@@ -1,7 +1,7 @@
'use strict';
angular.module('copay.peer').controller('PeerController',
- function($scope, $rootScope, $location, $routeParams, Network) {
+ function($scope, $rootScope, $location, $routeParams) {
$scope.init = function() {
//Network.connect($rootScope.masterId);
diff --git a/js/controllers/send.js b/js/controllers/send.js
index 8b2bf9e0a..682d89dc9 100644
--- a/js/controllers/send.js
+++ b/js/controllers/send.js
@@ -1,7 +1,7 @@
'use strict';
angular.module('copay.send').controller('SendController',
- function($scope, $rootScope, $location, Network) {
+ function($scope, $rootScope, $location) {
$scope.title = 'Send';
if (!$rootScope.wallet.id) {
@@ -11,27 +11,8 @@ angular.module('copay.send').controller('SendController',
$scope.sendTest = function() {
var w = $rootScope.wallet;
- var pkr = w.publicKeyRing;
- var txp = w.txProposals;
- var opts = {remainderOut: { address: pkr.generateAddress(true).toString() }};
-
- // From @cmgustavo's wallet
- w.listUnspent(function (unspentTest) {
-console.log('[send.js.19:unspentTest:]',unspentTest); //TODO
-
- txp.create(
- '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
- '123456789',
- unspentTest,
- w.privateKey,
- opts
- );
-console.log('[send.js.29:txp:] READY:',txp); //TODO
-
- Network.storeOpenWallet();
- Network.sendTxProposals();
- $rootScope.$digest;
-
- });
+ w.createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '12345',function() {
+ $rootScope.$digest();
+ });
};
});
diff --git a/js/controllers/setup.js b/js/controllers/setup.js
new file mode 100644
index 000000000..a8f25cb3e
--- /dev/null
+++ b/js/controllers/setup.js
@@ -0,0 +1,48 @@
+'use strict';
+
+angular.module('copay.setup').controller('SetupController',
+ function($scope, $rootScope, $location, walletFactory) {
+
+ $scope.loading = false;
+
+ $scope.totalCopayers = config.wallet.totalCopayers;
+ $scope.TCValues = [];
+ for (var n = 1; n <= config.limits.totalCopayers; n++)
+ $scope.TCValues.push(n);
+
+ var updateRCSelect = function(n) {
+ $scope.requiredCopayers = parseInt(Math.min(n / 2 + 1, config.limits.mPlusN-n));
+ $scope.RCValues = [];
+ for (var m = 1; m <= n; m++) {
+ if (n + m <= config.limits.mPlusN) {
+ $scope.RCValues.push(m);
+ }
+ }
+ };
+ updateRCSelect($scope.totalCopayers);
+
+ $scope.$watch('totalCopayers', function(tc) {
+ updateRCSelect(tc);
+ })
+
+ $scope.create = function(totalCopayers, requiredCopayers) {
+ $scope.loading = true;
+ var opts = {
+ requiredCopayers: requiredCopayers,
+ totalCopayers: totalCopayers
+ };
+ var w = walletFactory.create(opts);
+ w.on('created', function(){
+ $location.path('peer');
+ $rootScope.wallet = w;
+ $rootScope.$digest();
+ });
+ w.on('openError', function(){
+ $scope.loading = false;
+ $rootScope.flashMessage = {type:'error', message: 'Wallet not found'};
+ $location.path('signin');
+ });
+ w.netStart();
+ };
+
+ });
diff --git a/js/controllers/signin.js b/js/controllers/signin.js
index 62fca1008..2a5c00890 100644
--- a/js/controllers/signin.js
+++ b/js/controllers/signin.js
@@ -1,7 +1,7 @@
'use strict';
angular.module('copay.signin').controller('SigninController',
- function($scope, $rootScope, $location, Network) {
+ function($scope, $rootScope, $location, walletFactory) {
// var peerData = Storage.get($rootScope.walletId, 'peerData');
// $rootScope.peerId = peerData ? peerData.peerId : null;
@@ -10,55 +10,68 @@ angular.module('copay.signin').controller('SigninController',
$scope.selectedWalletId = false;
$scope.listWalletIds = function() {
- return copay.Wallet.factory.getWalletIds();
+ return walletFactory.getWalletIds();
+ };
+
+ var _setupUxHandlers = function(w) {
+ w.on('created', function() {
+ $location.path('peer');
+ $rootScope.wallet = w;
+ $rootScope.$digest();
+ });
+ w.on('refresh', function() {
+
+console.log('[signin.js.23] RECEIVED REFRESH'); //TODO
+ $rootScope.$digest();
+ });
+
+ w.on('openError', function(){
+ $scope.loading = false;
+ $rootScope.flashMessage = {type:'error', message: 'Wallet not found'};
+ $location.path('signin');
+ });
};
$scope.create = function() {
- $scope.loading = true;
-
- Network.createWallet();
- Network.init(function() {
- $location.path('peer');
- $rootScope.$digest();
- });
+ $location.path('setup');
};
$scope.open = function(walletId) {
$scope.loading = true;
- Network.openWallet(walletId);
-
- if ($rootScope.wallet && $rootScope.wallet.id) {
- Network.init(function() {
- $location.path('peer');
- $rootScope.$digest();
- });
- }
- else {
- $scope.loading = false;
- $rootScope.flashMessage = {type:'error', message: 'Wallet not found'};
- $location.path('signin');
- }
+ var w = walletFactory.open(walletId);
+ _setupUxHandlers(w);
+ w.netStart();
};
$scope.join = function(cid) {
+console.log('[signin.js.42:join:]'); //TODO
$scope.loading = true;
-
- if (cid) {
- Network.init(function() {
- Network.connect(cid,
- function() {
- $location.path('peer');
- $rootScope.$digest();
- }, function() {
- $rootScope.flashMessage = { message: 'Connection refussed', type: 'error'};
- $location.path('home');
- $rootScope.$digest();
- });
- });
- }
+ walletFactory.connectTo(cid, function(w) {
+console.log('[signin.js.50]'); //TODO
+ _setupUxHandlers(w);
+ w.netStart();
+ });
};
+
+//
+// if (cid) {
+// var w = walletFactory.(walletId);
+ //TODO
+ // Network.init(null, function() {
+ // Network.connect(cid,
+ // function() {
+ // $location.path('peer');
+ // $rootScope.$digest();
+ // }, function() {
+ // $rootScope.flashMessage = { message: 'Connection refussed', type: 'error'};
+ // $location.path('home');
+ // $rootScope.$digest();
+ // });
+ // });
+// }
+
// if (peerData && peerData.peerId && peerData.connectedPeers.length > 0) {
// $rootScope.peerId = peerData.peerId;
// $scope.join(peerData.connectedPeers);
diff --git a/js/controllers/transactions.js b/js/controllers/transactions.js
index beaf553cb..b92a6b409 100644
--- a/js/controllers/transactions.js
+++ b/js/controllers/transactions.js
@@ -24,4 +24,11 @@ angular.module('copay.transactions').controller('TransactionsController',
];
$scope.txsoutput = $rootScope.wallet.getTxProposals();
+
+ $scope.sign = function (ntxid) {
+ var w = $rootScope.wallet;
+ var ret = w.sign(ntxid);
+console.log('[transactions.js.28:ret:]',ret); //TODO
+ $scope.txsoutput = $rootScope.wallet.getTxProposals();
+ };
});
diff --git a/js/models/blockchain/Insight.js b/js/models/blockchain/Insight.js
index d45be6266..b7f11fad4 100644
--- a/js/models/blockchain/Insight.js
+++ b/js/models/blockchain/Insight.js
@@ -59,7 +59,7 @@ Insight.prototype.listUnspent = function(addresses, cb) {
self._request(options, function(err, res) {
if (res && res.length > 0) {
all = all.concat(res);
- }
+ }
callback();
});
}, function() {
diff --git a/js/models/core/Wallet.js b/js/models/core/Wallet.js
index 39ba995ff..8484da099 100644
--- a/js/models/core/Wallet.js
+++ b/js/models/core/Wallet.js
@@ -7,102 +7,147 @@ var coinUtil = bitcore.util;
var buffertools = bitcore.buffertools;
var Builder = bitcore.TransactionBuilder;
var http = require('http');
-
-var Storage = imports.Storage;
-var Network = imports.Network;
-var Blockchain = imports.Blockchain;
-
+var EventEmitter= imports.EventEmitter || require('events').EventEmitter;
var copay = copay || require('../../../copay');
-function Wallet(config) {
- this._startInterface(config);
-}
+function Wallet(opts) {
+ var self = this;
-Wallet.prototype.log = function(){
- if (!this.verbose) return;
+ //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.this.log(arguments);
-}
+ console.log('creating '+opts.requiredCopayers+' of '+opts.totalCopayers+' wallet');
-Wallet.prototype._startInterface = function(config) {
- this.storage = new Storage(config.storage);
- this.network = new Network(config.network);
- this.blockchain = new Blockchain(config.blockchain);
-
- this.networkName = config.networkName;
- this.requiredCopayers = config.wallet.requiredCopayers;
- this.totalCopayers = config.wallet.totalCopayers;
-};
-
-
-Wallet.prototype.create = function(opts) {
- opts = opts || {};
this.id = opts.id || Wallet.getRandomId();
- this.log('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID'));
+ this.verbose = opts.verbose;
+ this.publicKeyRing.walletId = this.id;
+ this.txProposals.walletId = this.id;
- this.privateKey = new copay.PrivateKey({
- networkName: this.networkName
- });
- this.log('\t### PrivateKey Initialized');
-
- this.publicKeyRing = new copay.PublicKeyRing({
- walletId: this.id,
- requiredCopayers: opts.requiredCopayers || this.requiredCopayers,
- totalCopayers: opts.totalCopayers || this.totalCopayers,
- networkName: this.networkName,
- });
-
- this.publicKeyRing.addCopayer(this.privateKey.getBIP32().extendedPublicKeyString());
- this.log('\t### PublicKeyRing Initialized WalletID: ' + this.publicKeyRing.walletId);
-
- this.txProposals = new copay.TxProposals({
- walletId: this.id,
- networkName: this.networkName,
- });
- this.log('\t### TxProposals Initialized');
-};
-
-
-Wallet.prototype._checkLoad = function(walletId) {
- var ret = this.storage.get(walletId, 'publicKeyRing') &&
- this.storage.get(walletId, 'txProposals') &&
- this.storage.get(walletId, 'privateKey')
- ;
- return ret;
}
-Wallet.prototype.load = function(walletId) {
- if (! this._checkLoad(walletId)) return;
+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;
+};
- this.id = walletId;
- this.publicKeyRing = new copay.PublicKeyRing.fromObj(
- this.storage.get(this.id, 'publicKeyRing')
- );
- this.txProposals = new copay.TxProposals.fromObj(
- this.storage.get(this.id, 'txProposals')
- );
- this.privateKey = new copay.PrivateKey.fromObj(
- this.storage.get(this.id, 'privateKey')
- ); //TODO secure
+Wallet.prototype._handlePublicKeyRing = function(senderId, data, isInbound) {
+ this.log('RECV PUBLICKEYRING:',data);
- // JIC: Add our key
- try {
- this.publicKeyRing.addCopayer(
- this.privateKey.getBIP32().extendedPublicKeyString()
- );
- } catch (e) {
- this.log('NOT NECCESARY AN ERROR:', e); //TODO
- };
+ 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');
+
+console.log('[Wallet.js.98]' , data.type); //TODO
+
+ 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) {
+ if (!newPeer) return;
+ 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() {
- Wallet.factory.addWalletId(this.id);
+console.log('[Wallet.js.135:store:]'); //TODO
+ 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());
+
+console.log('[Wallet.js.146] EMIT REFRESH'); //TODO
+ this.emit('refresh');
};
@@ -114,8 +159,20 @@ Wallet.prototype.sendTxProposals = function(recipients) {
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());
@@ -124,38 +181,82 @@ Wallet.prototype.sendPublicKeyRing = function(recipients) {
publicKeyRing: this.publicKeyRing.toObj(),
walletId: this.id,
});
+ this.emit('publicKeyRingUpdated', this.publicKeyRing);
};
Wallet.prototype.generateAddress = function() {
var addr = this.publicKeyRing.generateAddress();
this.store();
-
- this.network.send(null, {
- type: 'publicKeyRing',
- publicKeyRing: this.publicKeyRing.toObj(),
- walletId: this.id
- });
-
+ this.sendPublicKeyRing();
return addr;
};
Wallet.prototype.getTxProposals = function() {
var ret = [];
- this.txProposals.txps.forEach(function(txp) {
+ var self= this;
+ self.txProposals.txps.forEach(function(txp) {
var i = {txp:txp};
- i.signedByUs = txp.signedBy[this.privateKey.id]?true:false;
+ 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 always go to storage
+ * var wallet = WF.create(config,walletId); -> create wallets, with the given ID (or random is not given)
+ *
+ * var wallet = WF.open(config,walletId); -> try to read walletId, if fails, create a new wallet with that id
+ */
+
+function WalletFactory(config) {
+ var self = this;
+ this.storage = new Storage(config.storage);
+ this.network = new Network(config.network);
+ this.blockchain = new Blockchain(config.blockchain);
+
+ this.networkName = config.networkName;
+ this.verbose = config.verbose;
+ this.walletDefaults = config.wallet;
+}
+
+WalletFactory.prototype.log = function(){
+ if (!this.verbose) return;
+ console.log(arguments);
+};
+
+
+WalletFactory.prototype._checkRead = function(walletId) {
+ var s = this.storage;
+ var ret = s.get(walletId, 'publicKeyRing') &&
+ s.get(walletId, 'txProposals') &&
+ s.get(walletId, 'opts') &&
+ s.get(walletId, 'privateKey')
+ ;
+ return ret;
+};
+
+WalletFactory.prototype.read = function(walletId) {
+ if (! this._checkRead(walletId)) return false;
+
+ var s = this.storage;
+ var opts = s.get(walletId, 'opts');
+
+ opts.publicKeyRing = new PublicKeyRing.fromObj(s.get(walletId, 'publicKeyRing'));
+ opts.txProposals = new TxProposals.fromObj(s.get(walletId, 'txProposals'));
+ opts.privateKey = new PrivateKey.fromObj(s.get(walletId, 'privateKey'));
+
+ opts.storage = this.storage;
+ opts.network = this.network;
+ opts.blockchain = this.blockchain;
+ opts.verbose = this.verbose;
+
+ var w = new Wallet(opts);
+
+ // JIC: Add our key
+ try {
+ w.publicKeyRing.addCopayer(
+ w.privateKey.getBIP32().extendedPublicKeyString()
+ );
+ } catch (e) {
+ this.log('NOT NECCESARY AN ERROR:', e); //TODO
+ }
+ this.log('### WALLET OPENED:', w.id);
+ return w;
+};
+
+WalletFactory.prototype.create = function(opts) {
+ var s = WalletFactory.storage;
+ opts = opts || {};
+ this.log('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID'));
+
+ opts.privateKey = opts.privateKey || new PrivateKey({ networkName: this.networkName });
+ this.log('\t### PrivateKey Initialized');
+
+ var requiredCopayers = opts.requiredCopayers || this.walletDefaults.requiredCopayers;
+ var totalCopayers = opts.totalCopayers || this.walletDefaults.totalCopayers;
+
+ opts.publicKeyRing = opts.publicKeyRing || new PublicKeyRing({
+ networkName: this.networkName,
+ requiredCopayers: requiredCopayers,
+ totalCopayers: totalCopayers,
+ });
+ opts.publicKeyRing.addCopayer(opts.privateKey.getBIP32().extendedPublicKeyString());
+ this.log('\t### PublicKeyRing Initialized');
+
+ opts.txProposals = opts.txProposals || new TxProposals({
+ networkName: this.networkName,
+ });
+ this.log('\t### TxProposals Initialized');
+
+ opts.storage = this.storage;
+ opts.network = this.network;
+ opts.blockchain = this.blockchain;
+ opts.verbose = this.verbose;
+
+ opts.spendUnconfirmed = opts.spendUnconfirmed || this.walletDefaults.spendUnconfirmed;
+ opts.requiredCopayers = requiredCopayers;
+ opts.totalCopayers = totalCopayers;
+ var w = new Wallet(opts);
+ w.store();
+ return w;
+};
+
+WalletFactory.prototype.open = function(walletId) {
+ var w = this.read(walletId) || this.create({
+ id: walletId,
+ verbose: this.verbose,
+ });
+ return w;
+};
+
+WalletFactory.prototype.openRemote = function(peedId) {
+ var s = WalletFactory.storage;
+ opts = opts || {};
+ this.log('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID'));
+
+ opts.privateKey = opts.privateKey || new PrivateKey({ networkName: this.networkName });
+ this.log('\t### PrivateKey Initialized');
+
+ var requiredCopayers = opts.requiredCopayers || this.walletDefaults.requiredCopayers;
+ var totalCopayers = opts.totalCopayers || this.walletDefaults.totalCopayers;
+
+ opts.publicKeyRing = opts.publicKeyRing || new PublicKeyRing({
+ networkName: this.networkName,
+ requiredCopayers: requiredCopayers,
+ totalCopayers: totalCopayers,
+ });
+ opts.publicKeyRing.addCopayer(opts.privateKey.getBIP32().extendedPublicKeyString());
+ this.log('\t### PublicKeyRing Initialized');
+
+ opts.txProposals = opts.txProposals || new TxProposals({
+ networkName: this.networkName,
+ });
+ this.log('\t### TxProposals Initialized');
+
+ opts.storage = this.storage;
+ opts.network = this.network;
+ opts.blockchain = this.blockchain;
+ opts.spendUnconfirmed = opts.spendUnconfirmed || this.walletDefaults.spendUnconfirmed;
+ opts.requiredCopayers = requiredCopayers;
+ opts.totalCopayers = totalCopayers;
+ var w = new Wallet(opts);
+ w.store();
+ this.addWalletId(w.id);
+ return w;
+};
+
+WalletFactory.prototype.getWalletIds = function() {
+ return this.storage.getWalletIds();
+}
+
+WalletFactory.prototype.remove = function(walletId) {
+ // TODO remove wallet contents
+};
+
+
+WalletFactory.prototype.connectTo = function(peerId, cb) {
+ var self=this;
+ self.network.start(function() {
+ self.network.connectTo(peerId)
+ self.network.on('walletId', function(walletId) {
+console.log('[WalletFactory.js.187]'); //TODO
+ return cb(self.open(walletId));
+ });
+ });
+};
+
+module.exports = require('soop')(WalletFactory);
diff --git a/js/models/network/WebRTC.js b/js/models/network/WebRTC.js
index f3efa6895..80171fba7 100644
--- a/js/models/network/WebRTC.js
+++ b/js/models/network/WebRTC.js
@@ -67,7 +67,6 @@ Network.prototype._showConnectedPeers = function() {
};
Network.prototype._onClose = function(peerId) {
- console.log('[Network.js.70] _onClose'); //TODO
this.connectedPeers = Network._arrayRemove(peerId, this.connectedPeers);
this._notify();
};
@@ -103,6 +102,9 @@ Network.prototype._onData = function(data, isInbound) {
case 'disconnect':
this._onClose(obj.sender);
break;
+ case 'walletId':
+ this.emit('walletId', obj.data.walletId);
+ break;
default:
this.emit('data', obj.sender, obj.data, isInbound);
}
@@ -131,8 +133,7 @@ Network.prototype._addPeer = function(peerId, isInbound) {
}
};
-Network.prototype._setupConnectionHandlers = function(
- dataConn, isInbound, openCallback, closeCallback) {
+Network.prototype._setupConnectionHandlers = function(dataConn, isInbound) {
var self=this;
@@ -144,7 +145,7 @@ Network.prototype._setupConnectionHandlers = function(
self._addPeer(dataConn.peer, isInbound);
self._notify( isInbound ? dataConn.peer : null);
- if (typeof openCallback === 'function') openCallback();
+ this.emit('open');
}
});
@@ -158,10 +159,10 @@ Network.prototype._setupConnectionHandlers = function(
dataConn.on('close', function() {
if (self.closing) return;
+ self.closing=1;
console.log('### CLOSE RECV FROM:', dataConn.peer);
-
self._onClose(dataConn.peer);
- if (typeof closeCallback === 'function') closeCallback();
+ this.emit('close');
});
};
@@ -174,7 +175,6 @@ Network.prototype._setupPeerHandlers = function(openCallback) {
var self=this;
var p = this.peer;
-
p.on('open', function(peerId) {
self.peerId = peerId;
self.connectedPeers = [peerId];
@@ -208,6 +208,8 @@ Network.prototype._setupPeerHandlers = function(openCallback) {
Network.prototype.start = function(openCallback) {
// Start PeerJS Peer
+ if (this.peer) return openCallback(); // This is for connectTo-> peer is started before
+
this.peer = new Peer(this.peerId, {
key: this.apiKey, // TODO: we need our own PeerServer KEY (http://peerjs.com/peerserver)
debug: this.debug,
@@ -218,8 +220,6 @@ Network.prototype.start = function(openCallback) {
Network.prototype._sendToOne = function(peerId, data, cb) {
if (peerId !== this.peerId) {
-console.log('[WebRTC.js.222:peerId:]',peerId, data); //TODO
-
var conns = this.peer.connections[peerId];
if (conns) {
@@ -257,7 +257,7 @@ Network.prototype.send = function(peerIds, data, cb) {
self._sendToOne(peerIds, data, cb);
};
-Network.prototype.connectTo = function(peerId, openCallback, closeCallback ) {
+Network.prototype.connectTo = function(peerId) {
var self = this;
console.log('### STARTING TO CONNECT TO:' + peerId );
@@ -269,11 +269,11 @@ Network.prototype.connectTo = function(peerId, openCallback, closeCallback ) {
metadata: { message: 'hi copayer!' }
});
- self._setupConnectionHandlers(dataConn, false, openCallback, closeCallback);
+ self._setupConnectionHandlers(dataConn, false);
};
-Network.prototype.disconnect = function(peerId, cb) {
+Network.prototype.disconnect = function(cb) {
var self = this;
self.closing = 1;
diff --git a/js/models/storage/Base.js b/js/models/storage/Base.js
index 8f61f4dfe..29ca2564c 100644
--- a/js/models/storage/Base.js
+++ b/js/models/storage/Base.js
@@ -17,6 +17,9 @@ Storage.prototype.set = function(walletId,v) {
Storage.prototype.remove = function(walletId, k) {
};
+Storage.prototype.getWalletIds = function() {
+};
+
// remove all values
Storage.prototype.clearAll = function() {
};
diff --git a/js/models/storage/File.js b/js/models/storage/File.js
new file mode 100644
index 000000000..2c07b7010
--- /dev/null
+++ b/js/models/storage/File.js
@@ -0,0 +1,102 @@
+'use strict';
+var imports = require('soop').imports();
+var fs = imports.fs || require('fs');
+
+function Storage(opts) {
+ opts = opts || {};
+
+ this.data = {};
+}
+
+Storage.prototype.load = function(walletId, callback) {
+ fs.readFile(walletId, function(err, data) {
+ if (err) return callback(err);
+
+ try {
+ this.data[walletId] = JSON.parse(data);
+ } catch (err) {
+ if (callback)
+ return callback(err);
+ }
+
+ if (callback)
+ return callback(null);
+ });
+};
+
+Storage.prototype.save = function(walletId, callback) {
+ var data = JSON.stringify(this.data[walletId]);
+
+ //TODO: update to use a queue to ensure that saves are made sequentially
+ fs.writeFile(walletId, data, function(err) {
+ if (callback)
+ return callback(err);
+ });
+};
+
+Storage.prototype._read = function(k) {
+ var split = k.split('::');
+ var walletId = split[0];
+ var key = split[1];
+ return this.data[walletId][key];
+};
+
+Storage.prototype._write = function(k, v, callback) {
+ var split = k.split('::');
+ var walletId = split[0];
+ var key = split[1];
+ if (!this.data[walletId])
+ this.data[walletId] = {};
+ this.data[walletId][key] = v;
+ this.save(walletId, callback);
+};
+
+// get value by key
+Storage.prototype.getGlobal = function(k) {
+ return this._read(k);
+};
+
+// set value for key
+Storage.prototype.setGlobal = function(k, v, callback) {
+ this._write(k, v, callback);
+};
+
+// remove value for key
+Storage.prototype.removeGlobal = function(k, callback) {
+ var split = k.split('::');
+ var walletId = split[0];
+ var key = split[1];
+ delete this.data[walletId][key];
+ this.save(walletId, callback);
+};
+
+Storage.prototype._key = function(walletId, k) {
+ return walletId + '::' + k;
+};
+
+// get value by key
+Storage.prototype.get = function(walletId, k) {
+ return this.getGlobal(this._key(walletId, k));
+};
+
+// set value for key
+Storage.prototype.set = function(walletId, k, v, callback) {
+ this.setGlobal(this._key(walletId,k), v, callback);
+};
+
+// remove value for key
+Storage.prototype.remove = function(walletId, k, callback) {
+ this.removeGlobal(this._key(walletId, k), callback);
+};
+
+Storage.prototype.getWalletIds = function() {
+ return [];
+};
+
+// remove all values
+Storage.prototype.clearAll = function(callback) {
+ this.data = {};
+ this.save(callback);
+};
+
+module.exports = require('soop')(Storage);
diff --git a/js/models/storage/LocalPlain.js b/js/models/storage/LocalPlain.js
index 1bda410f2..d32234899 100644
--- a/js/models/storage/LocalPlain.js
+++ b/js/models/storage/LocalPlain.js
@@ -52,6 +52,21 @@ Storage.prototype.remove = function(walletId, k) {
this.removeGlobal(this._key(walletId,k));
};
+Storage.prototype.getWalletIds = function() {
+ var walletIds = [];
+
+ for (var i = 0; i < localStorage.length; i++) {
+ var key = localStorage.key(i);
+ var split = key.split('::');
+ if (split.length == 2) {
+ var walletId = split[0];
+ walletIds.push(walletId);
+ }
+ }
+
+ return walletIds;
+};
+
// remove all values
Storage.prototype.clearAll = function() {
localStorage.clear();
diff --git a/js/routes.js b/js/routes.js
index 3793d88bd..5c655b208 100644
--- a/js/routes.js
+++ b/js/routes.js
@@ -12,6 +12,9 @@ angular
.when('/signin', {
templateUrl: 'signin.html'
})
+ .when('/setup', {
+ templateUrl: 'setup.html'
+ })
.when('/home', {
templateUrl: 'home.html'
})
diff --git a/js/services/network.js b/js/services/network.js
deleted file mode 100644
index a4e69dac7..000000000
--- a/js/services/network.js
+++ /dev/null
@@ -1,204 +0,0 @@
-'use strict';
-
-angular.module('copay.network')
- .factory('Network', function($rootScope) {
- var peer;
-
- var _refreshUx = function() {
- var net = $rootScope.wallet.network;
- log('*** UPDATING UX'); //TODO
- $rootScope.peedId = net.peerId;
- $rootScope.connectedPeers = net.connectedPeers;
- $rootScope.$digest();
- };
-
- // set new inbound connections
- var _setNewPeer = function(newPeer) {
- var w = $rootScope.wallet;
- log('#### Setting new PEER:', newPeer);
- w.sendPublicKeyRing(newPeer);
- w.sendTxProposals(newPeer);
- };
-
- var _handleNetworkChange = function(newPeer) {
- var cp = $rootScope.cp;
-
- if (newPeer)
- _setNewPeer(newPeer);
-
- _refreshUx();
- };
-
- // TODO -> probably not in network.js
- var storeOpenWallet = function() {
- var w = $rootScope.wallet;
- w.store();
- log('\t### Wallet %s Stored', w.id);
- };
-
- // TODO -> probably not in network.js
- var createWallet = function(walletId) {
- var w = $rootScope.wallet || new copay.Wallet(config);
- w.create({id: walletId});
- w.store();
- $rootScope.wallet = w;
- log('createWallet ENDED'); //TODO
- };
-
- var openWallet = function (walletId) {
- var w = $rootScope.wallet || new copay.Wallet(config);
- w.load(walletId);
- if (w && w.publicKeyRing && w.privateKey) {
- log('### WALLET OPENED:', w.walletId);
- $rootScope.wallet = w;
- }
- };
-
-
- var closeWallet = function() {
- var w = $rootScope.wallet;
- if (w && w.id)
- w.store();
-
- log('### CLOSING WALLET');
- delete $rootScope['wallet'];
- };
-
- var _checkWallet = function(walletId) {
- log('[network.js.79:_checkWallet:]',walletId); //TODO
-
- if ($rootScope.wallet && $rootScope.wallet.id === walletId)
- return;
-
- if ($rootScope.wallet && $rootScope.wallet.id && $rootScope.wallet.id !== walletId) {
- throw new Error('message to wrong walletID');
- }
-
- if (!openWallet(walletId)) {
- createWallet(walletId);
- }
- };
-
- var _handlePublicKeyRing = function(senderId, data, isInbound) {
- _checkWallet(data.walletId);
- var shouldSend = false;
-
- var w = $rootScope.wallet;
- var recipients, pkr = w.publicKeyRing;
- var inPKR = copay.PublicKeyRing.fromObj(data.publicKeyRing);
- if (pkr.merge(inPKR, true) && !data.isBroadcast) {
- log('### BROADCASTING PKR');
- recipients = null;
- shouldSend = true;
- }
- else if (isInbound && !data.isBroadcast) {
- // always replying to connecting peer
- log('### REPLYING PKR TO:', senderId);
- recipients = senderId;
- shouldSend = true;
- }
-
- if (shouldSend) {
- w.sendPublicKeyRing(recipients);
- }
- _refreshUx();
- };
-
- var _handleTxProposals = function(senderId, data, isInbound) {
- _checkWallet(data.walletId);
-
- var shouldSend = false;
- var w = $rootScope.wallet;
- log('RECV TXPROPOSAL:',data); //TODO
-
- var recipients;
- var inTxProposals = copay.TxProposals.fromObj(data.txProposals);
- var mergeInfo = w.txProposals.merge(inTxProposals, true);
-
- var addSeen = w.addSeenToTxProposals();
- if ((mergeInfo.merged && !data.isBroadcast) || addSeen) {
- log('### BROADCASTING txProposals. ' );
- recipients = null;
- shouldSend = true;
- }
- else if (isInbound && !data.isBroadcast) {
- // always replying to connecting peer
- log('### REPLYING txProposals TO:', senderId);
- recipients = senderId;
- shouldSend = true;
- }
-
- if (shouldSend) {
- w.sendTxProposals(recipients);
- }
- };
-
- var _handleData = function(senderId, data, isInbound) {
-
- switch(data.type) {
- case 'publicKeyRing':
- _handlePublicKeyRing(senderId, data, isInbound);
- break;
- case 'txProposals':
- _handleTxProposals(senderId, data, isInbound);
- break;
- case 'abort':
- disconnect();
- _refreshUx();
- break;
- }
- };
-
- // public methods
- var init = function(cb) {
- if (!$rootScope.wallet) {
- // create an empty Wallet
- $rootScope.wallet = new copay.Wallet(config);
- }
- var net = $rootScope.wallet.network;
- net.on('networkChange', _handleNetworkChange);
- net.on('data', _handleData);
- net.start(function(peerId) {
- return cb();
- });
- };
-
- var disconnect = function() {
- var w = $rootScope.wallet;
- var net = w.network;
-
- if (net) {
- net.disconnect();
- }
- closeWallet();
- };
-
- var connect = function(peerId, openCallback, failCallback) {
- if ($rootScope.wallet.network) {
- $rootScope.wallet.network.connectTo(peerId, openCallback, function () {
- disconnect();
- failCallback();
- });
- }
- else
- return failCallback();
- };
-
- var sendTxProposals = function(recipients) {
- var w = $rootScope.wallet;
- w.sendTxProposals(recipients);
- };
-
- return {
- init: init,
- connect: connect,
- disconnect: disconnect,
-
- createWallet: createWallet,
- openWallet: openWallet,
- storeOpenWallet: storeOpenWallet,
-
- sendTxProposals: sendTxProposals,
- }
- });
-
diff --git a/js/services/walletFactory.js b/js/services/walletFactory.js
new file mode 100644
index 000000000..2264f104b
--- /dev/null
+++ b/js/services/walletFactory.js
@@ -0,0 +1,8 @@
+'use strict';
+
+var wf;
+angular.module('copay.walletFactory').factory('walletFactory', function($rootScope) {
+ wf = wf || new copay.WalletFactory(config);
+ return wf;
+});
+
diff --git a/lib/bitcore.js b/lib/bitcore.js
new file mode 100644
index 000000000..3ef2263bb
--- /dev/null
+++ b/lib/bitcore.js
@@ -0,0 +1,375 @@
+require=
+// modules are defined as an array
+// [ module function, map of requireuires ]
+//
+// map of requireuires is short require name -> numeric require
+//
+// anything defined in a previous bundle is accessed via the
+// orig method which is the requireuire for previous bundles
+(function outer (modules, cache, entry) {
+ // Save the require from previous bundle to this closure if any
+ var previousRequire = typeof require == "function" && require;
+
+ function newRequire(name, jumped, inSkipCache){
+
+ var m, skipCache = inSkipCache;
+ if (typeof name === 'string') {
+ if (name.charAt(0) === '!' ) {
+ name = name.substr(1);
+ skipCache=true;
+ }
+ }
+ if(skipCache || !cache[name]) {
+ if(!modules[name]) {
+ // if we cannot find the the module within our internal map or
+ // cache jump to the current global require ie. the last bundle
+ // that was added to the page.
+ var currentRequire = typeof require == "function" && require;
+ if (!jumped && currentRequire) return currentRequire(name, true);
+
+ // If there are other bundles on this page the require from the
+ // previous one is saved to 'previousRequire'. Repeat this as
+ // many times as there are bundles until the module is found or
+ // we exhaust the require chain.
+ if (previousRequire) return previousRequire(name, true);
+ throw new Error('Cannot find module \'' + name + '\'');
+ }
+
+ m = {exports:{}};
+ var nextSkipCache = inSkipCache ? false : skipCache;
+ if (!skipCache) cache[name] = m;
+ skipCache = false;
+ modules[name][0].call(m.exports, function(x){
+ var id = modules[name][1][x];
+ return newRequire(id ? id : x, false, nextSkipCache);
+ },m,m.exports,outer,modules,cache,entry);
+ }
+ return m ? m.exports:cache[name].exports;
+ }
+ for(var i=0;in;n++)t*=256,t+=e[n];return t}function t(e){return i(e,1)}function n(e){return i(e,4)}var r=require("soop").imports(),s=r.base58||require("base58-native").base58,h=r.coinUtil||require("./util/util"),d=r.Key||require("./Key"),c=r.Point||require("./Point"),a=r.bignum||require("bignum"),o=(require("crypto"),require("./networks")),y=new a("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",16),l=(new a("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",16),function(i){if("mainnet"==i||"livenet"==i?this.version=o.livenet.bip32privateVersion:"testnet"==i&&(this.version=o.testnet.bip32privateVersion),"mainnet"==i||"livenet"==i||"testnet"==i)return this.depth=0,this.parentFingerprint=new e([0,0,0,0]),this.childIndex=new e([0,0,0,0]),this.chainCode=d.generateSync().private,this.eckey=d.generateSync(),this.hasPrivateKey=!0,this.pubKeyHash=h.sha256ripe160(this.eckey.public),this.buildExtendedPublicKey(),void this.buildExtendedPrivateKey();if("string"==typeof i){var t=s.decode(i);if(82!=t.length)throw new Error("Not enough data");var n=t.slice(78,82);i=t.slice(0,78);var r=h.sha256(h.sha256(i));if(r[0]!=n[0]||r[1]!=n[1]||r[2]!=n[2]||r[3]!=n[3])throw new Error("Invalid checksum")}void 0!==i&&this.initFromBytes(i)});l.seed=function(i,t){if(t||(t="livenet"),e.isBuffer(i)||(i=new e(i,"hex")),i.length<16)return!1;var n=h.sha512hmac(i,new e("Bitcoin seed")),r=new l;return r.depth=0,r.parentFingerprint=new e([0,0,0,0]),r.childIndex=new e([0,0,0,0]),r.chainCode=n.slice(32,64),r.version=o[t].bip32privateVersion,r.eckey=new d,r.eckey.private=n.slice(0,32),r.eckey.regenerateSync(),r.hasPrivateKey=!0,r.pubKeyHash=h.sha256ripe160(r.eckey.public),r.buildExtendedPublicKey(),r.buildExtendedPrivateKey(),r},l.prototype.initFromBytes=function(e){if(78!=e.length)throw new Error("not enough data");this.version=n(e.slice(0,4)),this.depth=t(e.slice(4,5)),this.parentFingerprint=e.slice(5,9),this.childIndex=n(e.slice(9,13)),this.chainCode=e.slice(13,45);var i=e.slice(45,78),r=this.version==o.livenet.bip32privateVersion||this.version==o.testnet.bip32privateVersion,s=this.version==o.livenet.bip32publicVersion||this.version==o.testnet.bip32publicVersion;if(r&&0==i[0])this.eckey=new d,this.eckey.private=i.slice(1,33),this.eckey.compressed=!0,this.eckey.regenerateSync(),this.pubKeyHash=h.sha256ripe160(this.eckey.public),this.hasPrivateKey=!0;else{if(!s||2!=i[0]&&3!=i[0])throw new Error("Invalid key");this.eckey=new d,this.eckey.public=i,this.pubKeyHash=h.sha256ripe160(this.eckey.public),this.hasPrivateKey=!1}this.buildExtendedPublicKey(),this.buildExtendedPrivateKey()},l.prototype.buildExtendedPublicKey=function(){this.extendedPublicKey=new e([]);var i=null;switch(this.version){case o.livenet.bip32publicVersion:case o.livenet.bip32privateVersion:i=o.livenet.bip32publicVersion;break;case o.testnet.bip32publicVersion:case o.testnet.bip32privateVersion:i=o.testnet.bip32publicVersion;break;default:throw new Error("Unknown version")}this.extendedPublicKey=e.concat([this.extendedPublicKey,new e([i>>24])]),this.extendedPublicKey=e.concat([this.extendedPublicKey,new e([i>>16&255])]),this.extendedPublicKey=e.concat([this.extendedPublicKey,new e([i>>8&255])]),this.extendedPublicKey=e.concat([this.extendedPublicKey,new e([255&i])]),this.extendedPublicKey=e.concat([this.extendedPublicKey,new e([this.depth])]),this.extendedPublicKey=e.concat([this.extendedPublicKey,this.parentFingerprint]),this.extendedPublicKey=e.concat([this.extendedPublicKey,new e([this.childIndex>>>24])]),this.extendedPublicKey=e.concat([this.extendedPublicKey,new e([this.childIndex>>>16&255])]),this.extendedPublicKey=e.concat([this.extendedPublicKey,new e([this.childIndex>>>8&255])]),this.extendedPublicKey=e.concat([this.extendedPublicKey,new e([255&this.childIndex])]),this.extendedPublicKey=e.concat([this.extendedPublicKey,this.chainCode]),this.extendedPublicKey=e.concat([this.extendedPublicKey,this.eckey.public])},l.prototype.extendedPublicKeyString=function(i){if(void 0===i||"base58"===i){var t=h.sha256(h.sha256(this.extendedPublicKey)),n=t.slice(0,4),r=e.concat([this.extendedPublicKey,n]);return s.encode(r)}if("hex"===i)return this.extendedPublicKey.toString("hex");throw new Error("bad format")},l.prototype.buildExtendedPrivateKey=function(){if(this.hasPrivateKey){this.extendedPrivateKey=new e([]);var i=this.version;this.extendedPrivateKey=e.concat([this.extendedPrivateKey,new e([i>>24])]),this.extendedPrivateKey=e.concat([this.extendedPrivateKey,new e([i>>16&255])]),this.extendedPrivateKey=e.concat([this.extendedPrivateKey,new e([i>>8&255])]),this.extendedPrivateKey=e.concat([this.extendedPrivateKey,new e([255&i])]),this.extendedPrivateKey=e.concat([this.extendedPrivateKey,new e([this.depth])]),this.extendedPrivateKey=e.concat([this.extendedPrivateKey,this.parentFingerprint]),this.extendedPrivateKey=e.concat([this.extendedPrivateKey,new e([this.childIndex>>>24])]),this.extendedPrivateKey=e.concat([this.extendedPrivateKey,new e([this.childIndex>>>16&255])]),this.extendedPrivateKey=e.concat([this.extendedPrivateKey,new e([this.childIndex>>>8&255])]),this.extendedPrivateKey=e.concat([this.extendedPrivateKey,new e([255&this.childIndex])]),this.extendedPrivateKey=e.concat([this.extendedPrivateKey,this.chainCode]),this.extendedPrivateKey=e.concat([this.extendedPrivateKey,new e([0])]),this.extendedPrivateKey=e.concat([this.extendedPrivateKey,this.eckey.private])}},l.prototype.extendedPrivateKeyString=function(i){if(void 0===i||"base58"===i){var t=h.sha256(h.sha256(this.extendedPrivateKey)),n=t.slice(0,4),r=e.concat([this.extendedPrivateKey,n]);return s.encode(r)}if("hex"===i)return this.extendedPrivateKey.toString("hex");throw new Error("bad format")},l.prototype.derive=function(e){var i=e.split("/");if("m"==e||"M"==e||"m'"==e||"M'"==e)return this;var t=this;for(var n in i){var r=i[n];if(0!=n){var s=r.length>1&&"'"==r[r.length-1],h=2147483647&parseInt(s?r.slice(0,r.length-1):r);s&&(h+=2147483648),t=t.deriveChild(h)}else if("m"!=r)throw new Error("invalid path")}return t},l.prototype.deriveChild=function(i){var t=[];t.push(i>>24&255),t.push(i>>16&255),t.push(i>>8&255),t.push(255&i),t=new e(t);var n=0!=(2147483648&i),r=this.version==o.livenet.bip32privateVersion||this.version==o.testnet.bip32privateVersion;if(n&&(!this.hasPrivateKey||!r))throw new Error("Cannot do private key derivation without private key");var s=null;if(this.hasPrivateKey){var u=null;u=e.concat(n?[new e([0]),this.eckey.private,t]:[this.eckey.public,t]);var v=h.sha512hmac(u,this.chainCode),p=a.fromBuffer(v.slice(0,32),{size:32}),b=v.slice(32,64),K=a.fromBuffer(this.eckey.private,{size:32}),x=p.add(K).mod(y);s=new l,s.chainCode=b,s.eckey=new d,s.eckey.private=x.toBuffer({size:32}),s.eckey.regenerateSync(),s.hasPrivateKey=!0}else{var u=e.concat([this.eckey.public,t]),v=h.sha512hmac(u,this.chainCode),p=a.fromBuffer(v.slice(0,32),{size:32}),b=v.slice(32,64),P=new d;P.private=p.toBuffer({size:32}),P.regenerateSync();var w=c.fromKey(P),f=new d;f.public=this.eckey.public;var F=c.fromKey(f),k=c.add(w,F).toKey().public;s=new l,s.chainCode=new e(b);var g=new d;g.public=k,s.eckey=g,s.hasPrivateKey=!1}return s.childIndex=i,s.parentFingerprint=this.pubKeyHash.slice(0,4),s.version=this.version,s.depth=this.depth+1,s.eckey.compressed=!0,s.pubKeyHash=h.sha256ripe160(s.eckey.public),s.buildExtendedPublicKey(),s.buildExtendedPrivateKey(),s},module.exports=require("soop")(l)}).call(this,require("buffer").Buffer);
+},{"./Key":"Af1Gom","./Point":"8PQ/SZ","./networks":"ULNIu2","./util/util":"ACyo5H","base58-native":"xtP2pj","bignum":"J4nwo8","buffer":75,"crypto":79,"soop":146}],"KqTx5b":[function(require,module,exports){
+(function(t){function e(t){"object"!=typeof t&&(t={}),this.hash=t.hash||null,this.prev_hash=t.prev_hash||i.NULL_HASH,this.merkle_root=t.merkle_root||i.NULL_HASH,this.timestamp=t.timestamp||0,this.bits=t.bits||0,this.nonce=t.nonce||0,this.version=t.version||0,this.height=t.height||0,this.size=t.size||0,this.active=t.active||!1,this.chainWork=t.chainWork||i.EMPTY_BUFFER,this.txs=t.txs||[]}var r=require("soop").imports(),i=r.util||require("./util/util"),o=(r.Debug1||function(){},r.Script||require("./Script")),s=r.Bignum||require("bignum"),h=(r.Binary||require("binary"),r.Step||require("step"),r.buffertools||require("buffertools")),n=r.Transaction||require("./Transaction"),a=n.In,c=n.Out,u=n.COINBASE_OP,l=r.VerificationError||require("./util/error").VerificationError,f={maxTimeOffset:7200,largestHash:s(2).pow(256)};e.prototype.getHeader=function(){var e=new t(80),r=0;return e.writeUInt32LE(this.version,r),r+=4,this.prev_hash.copy(e,r),r+=32,this.merkle_root.copy(e,r),r+=32,e.writeUInt32LE(this.timestamp,r),r+=4,e.writeUInt32LE(this.bits,r),r+=4,e.writeUInt32LE(this.nonce,r),r+=4,e},e.prototype.parse=function(t,e){if(this.version=t.word32le(),this.prev_hash=t.buffer(32),this.merkle_root=t.buffer(32),this.timestamp=t.word32le(),this.bits=t.word32le(),this.nonce=t.word32le(),this.txs=[],this.size=0,!e)for(var r=t.varInt(),i=0;r>i;i++){var o=new n;o.parse(t),this.txs.push(o)}},e.prototype.calcHash=function(){var t=this.getHeader();return i.twoSha256(t)},e.prototype.checkHash=function(){return this.hash&&this.hash.length?0==h.compare(this.calcHash(),this.hash):!1},e.prototype.getHash=function(){return this.hash&&this.hash.length||(this.hash=this.calcHash()),this.hash},e.prototype.checkProofOfWork=function(){var t=i.decodeDiffBits(this.bits),e=h.reverse(this.hash);if(h.compare(e,t)>0)throw new l("Difficulty target not met");return!0},e.prototype.getWork=function(){var t=i.decodeDiffBits(this.bits,!0);return f.largestHash.div(t.add(1))},e.prototype.checkTimestamp=function(){var t=(new Date).getTime()/1e3;if(this.timestamp>t+f.maxTimeOffset)throw new l("Timestamp too far into the future");return!0},e.prototype.checkTransactions=function(t){if(!Array.isArray(t)||t.length<=0)throw new l("No transactions");if(!t[0].isCoinBase())throw new l("First tx must be coinbase");for(var e=1;e1;s=Math.floor((s+1)/2)){for(var h=0;s>h;h+=2){var a=Math.min(h+1,s-1),c=r[o+h],u=r[o+a];r.push(i.twoSha256(t.concat([c,u])))}o+=s}return r},e.prototype.calcMerkleRoot=function(t){var e=this.getMerkleTree(t);return e[e.length-1]},e.prototype.checkMerkleRoot=function(e){if(!this.merkle_root||!this.merkle_root.length)throw new l("No merkle root");if(0!==h.compare(this.calcMerkleRoot(e),new t(this.merkle_root)))throw new l("Merkle root incorrect");return!0},e.prototype.checkBlock=function(t){if(!this.checkHash())throw new l("Block hash invalid");if(this.checkProofOfWork(),this.checkTimestamp(),t&&(this.checkTransactions(t),!this.checkMerkleRoot(t)))throw new l("Merkle hash invalid");return!0},e.getBlockValue=function(t){var e=s(50).mul(i.COIN);return e=e.div(s(2).pow(Math.floor(t/21e4)))},e.prototype.getBlockValue=function(){return e.getBlockValue(this.height)},e.prototype.toString=function(){return""},e.prototype.createCoinbaseTx=function(t){var e=new n;return e.ins.push(new a({s:i.EMPTY_BUFFER,q:4294967295,o:u})),e.outs.push(new c({v:i.bigIntToValue(this.getBlockValue()),s:o.createPubKeyOut(t).getBuffer()})),e},e.prototype.solve=function(t,e){var r=this.getHeader(),o=i.decodeDiffBits(this.bits);t.solve(r,o,e)},e.prototype.getStandardizedObject=function(t){var e={hash:i.formatHashFull(this.getHash()),version:this.version,prev_block:i.formatHashFull(this.prev_hash),mrkl_root:i.formatHashFull(this.merkle_root),time:this.timestamp,bits:this.bits,nonce:this.nonce,height:this.height};if(t){var r=this.getMerkleTree(t).map(function(t){return i.formatHashFull(t)});e.mrkl_root=r[r.length-1],e.n_tx=t.length;var o=80;o+=i.getVarIntSize(t.length),t=t.map(function(t){return t=t.getStandardizedObject(),o+=t.size,t}),e.size=o,e.tx=t,e.mrkl_tree=r}else e.size=this.size;return e},module.exports=require("soop")(e)}).call(this,require("buffer").Buffer);
+},{"./Script":"clx6XL","./Transaction":"RNv6Te","./util/error":155,"./util/util":"ACyo5H","bignum":"J4nwo8","binary":62,"buffer":75,"buffertools":"fugeBw","soop":146,"step":147}],"./Block":[function(require,module,exports){
+module.exports=require('KqTx5b');
+},{}],"ZnXdbH":[function(require,module,exports){
+function Bloom(){this.data="",this.hashFuncs=0}function ROTL32(t,n){return t<>32-n}function getBlockU32(t,n){var o=4*t,s=n[o+0]<<0|n[o+1]<<8|n[o+2]<<16|n[o+3]<<24;return s}function toInt(t){return~~t}function min(t,n){return n>t?t:n}var MAX_BLOOM_FILTER_SIZE=36e3,MAX_HASH_FUNCS=50,LN2SQUARED=.48045301391820144,LN2=.6931471805599453,bit_mask=[1,2,4,8,16,32,64,128];Bloom.prototype.hash=function(t,n){for(var o=t*(4294967295/(this.hashFuncs-1)),s=3432918353,a=461845907,h=n.length/4,i=-h;i;i++){var r=getBlockU32(i);r*=s,r=ROTLF32(r,15),r*=a,o^=r,o=ROTFL(o,13),o=5*o+3864292196}var e=n.slice(4*h),r=0;switch(3&n.length){case 3:r^=e[2]<<16;case 2:r^=e[1]<<8;case 1:r^=e[0],r*=s,r=ROTL32(r,15),r*=a,o^=r}return o^=n.length,o^=o>>16,o*=2246822507,o^=o>>13,o*=3266489909,o^=o>>16,o%(8*this.data.length)},Bloom.prototype.insert=function(t){for(var n=0;n>3]|=bit_mask[7&o]}},Bloom.prototype.contains=function(t){for(var n=0;n>3]&bit_mask[7&o]))return!1}return!0},Bloom.prototype.sizeOk=function(){return this.data.length<=MAX_BLOOM_FILTER_SIZE&&this.hashFuncs<=MAX_HASH_FUNCS},Bloom.prototype.init=function(t,n){var o=min(toInt(-1/LN2SQUARED*t*Math.log(n)),8*MAX_BLOOM_FILTER_SIZE)/8;this.data[o]=0,this.hashFuncs=min(toInt(8*this.data.length/t*LN2),MAX_HASH_FUNCS)},module.exports=require("soop")(Bloom);
+},{"soop":146}],"./Bloom":[function(require,module,exports){
+module.exports=require('ZnXdbH');
+},{}],"./Buffers.monkey":[function(require,module,exports){
+module.exports=require('jmkPxi');
+},{}],"jmkPxi":[function(require,module,exports){
+(function(f){exports.patch=function(t){t.prototype.skip=function(t){if(0!=t){if(t==this.length)return this.buffers=[],void(this.length=0);var s=this.pos(t);this.buffers=this.buffers.slice(s.buf),this.buffers[0]=new f(this.buffers[0].slice(s.offset)),this.length-=t}}}}).call(this,require("buffer").Buffer);
+},{"buffer":75}],"./Connection":[function(require,module,exports){
+module.exports=require('h81grl');
+},{}],"h81grl":[function(require,module,exports){
+(function(e){function t(e,r){t.super(this,arguments),this.socket=e,this.peer=r,this.active=!1,this.recvVer=0,this.sendVer=0,this.bestHeight=0,this.inbound=!!e.server,this.getaddr=!1,this.buffers=new c,(new Date).getTime()>1329696e6&&(this.recvVer=209,this.sendVer=209),this.setupHandlers()}var r=require("soop").imports(),s=r.config||require("./config"),n=r.log||require("./util/log"),i=r.network||require("./networks")[s.network],o=1e7,a=7e4,h=(r.Binary||require("binary"),r.Put||require("bufferput")),c=r.Buffers||require("buffers");require("./Buffers.monkey").patch(c);var d=require("./Block"),f=require("./Transaction"),u=r.util||require("./util/util"),p=r.Parser||require("./util/BinaryParser"),g=r.buffertools||require("buffertools"),l=r.doubleSha256||u.twoSha256,v=u.generateNonce(),b=6e4;t.parent=r.parent||require("events").EventEmitter,t.prototype.setupHandlers=function(){this.socket.addListener("connect",this.handleConnect.bind(this)),this.socket.addListener("error",this.handleError.bind(this)),this.socket.addListener("end",this.handleDisconnect.bind(this)),this.socket.addListener("data",function(e){var t=35;n.debug("["+this.peer+"] Recieved "+e.length+" bytes of data:"),n.debug("... "+g.toHex(e.slice(0,t>e.length?e.length:t))+(e.length>t?"...":""))}.bind(this)),this.socket.addListener("data",this.handleData.bind(this))},t.prototype.handleConnect=function(){this.inbound||this.sendVersion(),this.emit("connect",{conn:this,socket:this.socket,peer:this.peer})},t.prototype.handleError=function(e){110==e.errno||"ETIMEDOUT"==e.errno?n.info("connection timed out for "+this.peer):111==e.errno||"ECONNREFUSED"==e.errno?n.info("connection refused for "+this.peer):n.warn("connection with "+this.peer+" "+e.toString()),this.emit("error",{conn:this,socket:this.socket,peer:this.peer,err:e})},t.prototype.handleDisconnect=function(){this.emit("disconnect",{conn:this,socket:this.socket,peer:this.peer})},t.prototype.handleMessage=function(t){if(t){try{switch(t.command){case"version":if(0===g.compare(v,t.nonce))return void this.socket.end();this.inbound&&this.sendVersion(),t.version>=209&&this.sendMessage("verack",new e([])),this.sendVer=Math.min(t.version,a),t.version<209?this.recvVer=Math.min(t.version,a):this.once("verack",function(){this.recvVer=t.version}.bind(this)),this.bestHeight=t.start_height;break;case"verack":this.recvVer=Math.min(t.version,a),this.active=!0;break;case"ping":"object"==typeof t.nonce&&this.sendPong(t.nonce)}}catch(r){return void n.err('Error while handling "'+t.command+'" message from '+this.peer+":\n"+(r.stack?r.stack:r.toString()))}this.emit(t.command,{conn:this,socket:this.socket,peer:this.peer,message:t})}},t.prototype.sendPong=function(e){this.sendMessage("pong",e)},t.prototype.sendVersion=function(){var t="/BitcoinX:0.1/",r=new h;r.word32le(a),r.word64le(1),r.word64le(Math.round((new Date).getTime()/1e3)),r.pad(26),r.pad(26),r.put(v),r.varint(t.length),r.put(new e(t,"ascii")),r.word32le(0),this.sendMessage("version",r.buffer())},t.prototype.sendGetBlocks=function(t,r,s){r=r||u.NULL_HASH;var n=new h;n.word32le(this.sendVer),n.varint(t.length);for(var i=0;i12)throw"Command name too long";var a;a=this.sendVer>=209?l(r).slice(0,4):new e([]);var c=new h;c.put(s),c.put(o),c.pad(12-o.length),c.word32le(r.length),c.put(a),c.put(r);var d=c.buffer();n.debug("["+this.peer+"] Sending message "+t+" ("+r.length+" bytes)"),this.socket.write(d)}catch(f){n.err("Error while sending message to peer "+this.peer+": "+(f.stack?f.stack:f.toString()))}},t.prototype.handleData=function(e){return this.buffers.push(e),this.buffers.length>o?(n.err("Peer "+this.peer+" exceeded maxreceivebuffer, disconnecting."+(err.stack?err.stack:err.toString())),void this.socket.destroy()):void this.processData()},t.prototype.processData=function(){if(!(this.buffers.length<20)){for(var e=i.magic,t=0;;){if(this.buffers.get(t)===e[0]&&this.buffers.get(t+1)===e[1]&&this.buffers.get(t+2)===e[2]&&this.buffers.get(t+3)===e[3]){0!==t&&(n.debug("["+this.peer+"] Received "+t+" bytes of inter-message garbage: "),n.debug("... "+this.buffers.slice(0,t)),this.buffers.skip(t));break}if(t>this.buffers.length-4)return void this.buffers.skip(t);t++}var r=this.buffers.get(16)+(this.buffers.get(17)<<8)+(this.buffers.get(18)<<16)+(this.buffers.get(19)<<24),s=this.recvVer>=209?24:20,o=s+r;if(!(this.buffers.length=209?this.buffers.slice(20,24):null;if(n.debug("["+this.peer+"] Received message "+a+" ("+r+" bytes)"),null!==c){var d=l(h).slice(0,4);if(0!==g.compare(d,c))return void n.err("["+this.peer+"] Checksum failed",{cmd:a,expected:d.toString("hex"),actual:c.toString("hex")})}var f;try{f=this.parseMessage(a,h)}catch(u){n.err("Error while parsing message "+a+" from "+this.peer+":\n"+(u.stack?u.stack:u.toString()))}f&&this.handleMessage(f),this.buffers.skip(o),this.processData()}}},t.prototype.parseMessage=function(e,t){var r,s=new p(t),i={command:e};switch(e){case"version":i.version=s.word32le(),i.services=s.word64le(),i.timestamp=s.word64le(),i.addr_me=s.buffer(26),i.addr_you=s.buffer(26),i.nonce=s.buffer(8),i.subversion=s.varStr(),i.start_height=s.word32le();break;case"inv":case"getdata":for(i.count=s.varInt(),i.invs=[],r=0;rr;r++)i.starts.push(s.buffer(32));i.stop=s.buffer(32);break;case"addr":var u=s.varInt();for(u>1e3&&(u=1e3),i.addrs=[],r=0;u>r;r++)i.addrs.push({time:s.word32le(),services:s.word64le(),ip:s.buffer(16),port:s.word16be()});break;case"alert":i.payload=s.varStr(),i.signature=s.varStr();break;case"ping":this.recvVer>b&&(i.nonce=s.buffer(8));break;case"getaddr":case"verack":case"reject":break;default:return n.err("Connection.parseMessage(): Command not implemented",{cmd:e}),null}return i},module.exports=require("soop")(t)}).call(this,require("buffer").Buffer);
+},{"./Block":"KqTx5b","./Buffers.monkey":"jmkPxi","./Transaction":"RNv6Te","./config":"4itQ50","./networks":"ULNIu2","./util/BinaryParser":"b3ZSD7","./util/log":"AdF7pF","./util/util":"ACyo5H","binary":62,"buffer":75,"bufferput":"aXRuS6","buffers":"OBo3aV","buffertools":"fugeBw","events":84,"soop":146}],"ypnq12":[function(require,module,exports){
+exports.intFromCompact=function(r){var t=(r>>>24&255)>>>0,n=(16777215&r)<<8*(t-3)>>>0;return n};
+},{}],"./Deserialize":[function(require,module,exports){
+module.exports=require('ypnq12');
+},{}],"./Gruntfile":[function(require,module,exports){
+module.exports=require('NbSoFu');
+},{}],"NbSoFu":[function(require,module,exports){
+"use strict";module.exports=function(s){s.loadNpmTasks("grunt-contrib-watch"),s.loadNpmTasks("grunt-mocha-test"),s.loadNpmTasks("grunt-markdown"),s.loadNpmTasks("grunt-shell"),s.initConfig({shell:{browserify:{options:{stdout:!0,stderr:!0},command:"dev"===s.option("target")?"node ./browser/build.js -a -d ":"node ./browser/build.js -a"}},watch:{readme:{files:["README.md"],tasks:["markdown"]},scripts:{files:["**/*.js","**/*.html","!**/node_modules/**","!browser/bundle.js","!browser/testdata.js","!browser/vendor-bundle.js"],tasks:["shell"]}},mochaTest:{options:{reporter:"spec"},src:["test/*.js"]},markdown:{all:{files:[{expand:!0,src:"README.md",dest:".",ext:".html"}]}}}),s.registerTask("default",["shell","watch"])};
+},{}],"./Key":[function(require,module,exports){
+module.exports=require('Af1Gom');
+},{}],"Af1Gom":[function(require,module,exports){
+(function(e,r){if(e.versions){var t=require("bindings")("KeyModule"),s=t.Key;module.exports=s}else{var o=require("./browser/vendor-bundle.js").ECKey,n=require("buffertools"),i=function(){this._pub=null,this.compressed=!0},u=i.bufferToArray=function(e){for(var r=[],t=e.length,s=0;t>s;s++)r.push(e.readUInt8(s));return r};Object.defineProperty(i.prototype,"public",{set:function(e){if(!r.isBuffer(e))throw new Error("Arg should be a buffer");var t=e[0];this.compressed=4!==t,this._pub=e},get:function(){return this._pub}}),i.generateSync=function(){var e=new o;e.setCompressed(!0);var t=e.getPub(),s=new i;return s.private=new r(e.priv.toByteArrayUnsigned()),s.public=new r(t),s},i.prototype.regenerateSync=function(){if(!this.private)throw new Error("Key does not have a private key set");var e=new o(n.toHex(this.private));return e.setCompressed(this.compressed),this.public=new r(e.getPub()),this},i.prototype.signSync=function(e){if(!this.private)throw new Error("Key does not have a private key set");if(!r.isBuffer(e)||32!==e.length)throw new Error("Arg should be a 32 bytes hash buffer");var t=new o(n.toHex(this.private));t.setCompressed(this.compressed);var s=t.sign(u(e));return new r(s)},i.prototype.verifySignature=function(e,r,t){try{var s=this.verifySignatureSync(e,r);t(null,s)}catch(o){t(o)}},i.prototype.verifySignatureSync=function(e,t){var s=this;if(!r.isBuffer(e)||32!==e.length)throw new Error("Arg 1 should be a 32 bytes hash buffer");if(!r.isBuffer(t))throw new Error("Arg 2 should be a buffer");if(!s.public)throw new Error("Key does not have a public key set");var n=new o;n.setPub(u(s.public)),n.setCompressed(s.compressed);var i=u(t),f=n.verify(u(e),i);return f},module.exports=i}}).call(this,require("/Users/ryanxcharles/dev/bitcore/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),require("buffer").Buffer);
+},{"./browser/vendor-bundle.js":53,"/Users/ryanxcharles/dev/bitcore/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":91,"bindings":66,"buffer":75,"buffertools":"fugeBw"}],"ahN55r":[function(require,module,exports){
+exports.patch=function(t){t.prototype.round=function(t){if(!t)return Math.round(this);var r=Math.pow(10,t);return Math.round(this*r)/r}};
+},{}],"./Number.monkey":[function(require,module,exports){
+module.exports=require('ahN55r');
+},{}],"./Opcode":[function(require,module,exports){
+module.exports=require('zHwnes');
+},{}],"zHwnes":[function(require,module,exports){
+function Opcode(O){this.code=O}var imports=require("soop").imports();Opcode.prototype.toString=function(){return Opcode.reverseMap[this.code]},Opcode.map={OP_FALSE:0,OP_0:0,OP_PUSHDATA1:76,OP_PUSHDATA2:77,OP_PUSHDATA4:78,OP_1NEGATE:79,OP_RESERVED:80,OP_TRUE:81,OP_1:81,OP_2:82,OP_3:83,OP_4:84,OP_5:85,OP_6:86,OP_7:87,OP_8:88,OP_9:89,OP_10:90,OP_11:91,OP_12:92,OP_13:93,OP_14:94,OP_15:95,OP_16:96,OP_NOP:97,OP_VER:98,OP_IF:99,OP_NOTIF:100,OP_VERIF:101,OP_VERNOTIF:102,OP_ELSE:103,OP_ENDIF:104,OP_VERIFY:105,OP_RETURN:106,OP_TOALTSTACK:107,OP_FROMALTSTACK:108,OP_2DROP:109,OP_2DUP:110,OP_3DUP:111,OP_2OVER:112,OP_2ROT:113,OP_2SWAP:114,OP_IFDUP:115,OP_DEPTH:116,OP_DROP:117,OP_DUP:118,OP_NIP:119,OP_OVER:120,OP_PICK:121,OP_ROLL:122,OP_ROT:123,OP_SWAP:124,OP_TUCK:125,OP_CAT:126,OP_SUBSTR:127,OP_LEFT:128,OP_RIGHT:129,OP_SIZE:130,OP_INVERT:131,OP_AND:132,OP_OR:133,OP_XOR:134,OP_EQUAL:135,OP_EQUALVERIFY:136,OP_RESERVED1:137,OP_RESERVED2:138,OP_1ADD:139,OP_1SUB:140,OP_2MUL:141,OP_2DIV:142,OP_NEGATE:143,OP_ABS:144,OP_NOT:145,OP_0NOTEQUAL:146,OP_ADD:147,OP_SUB:148,OP_MUL:149,OP_DIV:150,OP_MOD:151,OP_LSHIFT:152,OP_RSHIFT:153,OP_BOOLAND:154,OP_BOOLOR:155,OP_NUMEQUAL:156,OP_NUMEQUALVERIFY:157,OP_NUMNOTEQUAL:158,OP_LESSTHAN:159,OP_GREATERTHAN:160,OP_LESSTHANOREQUAL:161,OP_GREATERTHANOREQUAL:162,OP_MIN:163,OP_MAX:164,OP_WITHIN:165,OP_RIPEMD160:166,OP_SHA1:167,OP_SHA256:168,OP_HASH160:169,OP_HASH256:170,OP_CODESEPARATOR:171,OP_CHECKSIG:172,OP_CHECKSIGVERIFY:173,OP_CHECKMULTISIG:174,OP_CHECKMULTISIGVERIFY:175,OP_NOP1:176,OP_NOP2:177,OP_NOP3:178,OP_NOP4:179,OP_NOP5:180,OP_NOP6:181,OP_NOP7:182,OP_NOP8:183,OP_NOP9:184,OP_NOP10:185,OP_PUBKEYHASH:253,OP_PUBKEY:254,OP_INVALIDOPCODE:255},Opcode.reverseMap=[];for(var k in Opcode.map)Opcode.map.hasOwnProperty(k)&&(Opcode.reverseMap[Opcode.map[k]]=k.substr(3));Opcode.asList=function(){var O=[];for(var P in Opcode.map)Opcode.map.hasOwnProperty(P)&&O.push(P);return O},module.exports=require("soop")(Opcode);
+},{"soop":146}],"6VMrjH":[function(require,module,exports){
+(function(t){function e(r,o,i){if("string"==typeof r){if(r.indexOf(":")&&!o){var n=r.split(":");r=n[0],o=n[1]}this.host=r,this.port=+o||8333}else if(r instanceof e)this.host=r.host,this.port=r.port;else{if(!t.isBuffer(r))throw new Error("Could not instantiate peer, invalid parameter type: "+typeof r);if(0!=s.compare(e.IPV6_IPV4_PADDING,r.slice(0,12)))throw new Error("IPV6 not supported yet! Cannot instantiate host.");this.host=Array.prototype.slice.apply(r.slice(12)).join("."),this.port=+o||8333}this.services=i?i:null,this.lastSeen=0}var r=require("soop").imports(),o=r.Net||require("net"),i=r.Binary||require("binary"),s=r.buffertools||require("buffertools");e.IPV6_IPV4_PADDING=new t([0,0,0,0,0,0,0,0,0,0,255,255]),e.prototype.createConnection=function(){var t=o.createConnection(this.port,this.host);return t},e.prototype.getHostAsBuffer=function(){return new t(this.host.split("."))},e.prototype.toString=function(){return this.host+":"+this.port},e.prototype.toBuffer=function(){var t=i.put();return t.word32le(this.lastSeen),t.word64le(this.services),t.put(this.getHostAsBuffer()),t.word16be(this.port),t.buffer()},module.exports=require("soop")(e)}).call(this,require("buffer").Buffer);
+},{"binary":62,"buffer":75,"buffertools":"fugeBw","net":71,"soop":146}],"./Peer":[function(require,module,exports){
+module.exports=require('6VMrjH');
+},{}],"./PeerManager":[function(require,module,exports){
+module.exports=require('z6R5yC');
+},{}],"z6R5yC":[function(require,module,exports){
+function PeerManager(){this.active=!1,this.timer=null,this.peers=[],this.connections=[],this.isConnected=!1,this.peerDiscovery=!1,this.interval=5e3,this.minConnections=8,this.minKnownPeers=10}var imports=require("soop").imports(),config=imports.config||require("./config"),log=imports.log||require("./util/log"),network=imports.network||require("./networks")[config.network],Connection=imports.Connection||require("soop").load("./Connection",{config:config,network:network})||require("./Connection"),Peer=imports.Peer||require("./Peer");GetAdjustedTime=imports.GetAdjustedTime||function(){return Math.floor((new Date).getTime()/1e3)},PeerManager.parent=imports.parent||require("events").EventEmitter,PeerManager.Connection=Connection,PeerManager.prototype.start=function(){this.active=!0,this.timer||(this.timer=setInterval(this.checkStatus.bind(this),this.interval))},PeerManager.prototype.stop=function(){this.active=!1,this.timer&&(clearInterval(this.timer),this.timer=null);for(var e=0;e=31402||this.peers.length<1e3)&&(e.conn.sendGetAddr(),e.conn.getaddr=!0)},PeerManager.prototype.handleReady=function(e){log.info("connected to "+e.conn.peer.host+":"+e.conn.peer.port),this.emit("connect",{pm:this,conn:e.conn,socket:e.socket,peer:e.peer}),0==this.isConnected&&(this.emit("netConnected",e),this.isConnected=!0)},PeerManager.prototype.handleAddr=function(e){if(this.peerDiscovery){var n=GetAdjustedTime();e.message.addrs.forEach(function(e){try{(e.time<=1e8||e.time>n+600)&&(e.time=n-432e3);var t=new Peer(e.ip,e.port,e.services);t.lastSeen=e.time,this.peers.push(t)}catch(r){log.warn("Invalid addr received: "+r.message)}}.bind(this)),e.message.addrs.length<1e3&&(e.conn.getaddr=!1)}},PeerManager.prototype.handleGetAddr=function(){},PeerManager.prototype.handleError=function(e){log.err("unkown error with peer "+e.peer+" (disconnecting): "+e.err),this.handleDisconnect.apply(this,[].slice.call(arguments))},PeerManager.prototype.handleDisconnect=function(e){log.info("disconnected from peer "+e.peer);var n=this.connections.indexOf(e.conn);-1!=n&&this.connections.splice(n,1),this.connections.length||(this.emit("netDisconnected"),this.isConnected=!1)},PeerManager.prototype.getActiveConnection=function(){var e=this.connections.filter(function(e){return e.active});if(e.length){var n=Math.floor(Math.random()*e.length),t=e[n];return t.socket.writable?t:(e.splice(n,1),this.getActiveConnection())}return null},PeerManager.prototype.getActiveConnections=function(){return this.connections.slice(0)},module.exports=require("soop")(PeerManager);
+},{"./Connection":"h81grl","./Peer":"6VMrjH","./config":"4itQ50","./networks":"ULNIu2","./util/log":"AdF7pF","events":84,"soop":146}],"./Point":[function(require,module,exports){
+module.exports=require('8PQ/SZ');
+},{}],"8PQ/SZ":[function(require,module,exports){
+(function(e,r){"use strict";var s=require("soop").imports(),o=s.Key||require("./Key"),n=s.bignum||require("bignum"),t=require("assert");if(!e.versions){var i=require("./browser/vendor-bundle.js").ECKey,u=require("./browser/vendor-bundle.js").ECPointFp,f=require("./browser/vendor-bundle.js").ECFieldElementFp,d=require("./browser/vendor-bundle.js").getSECCurveByName,c=require("./browser/vendor-bundle.js").BigInteger;require("chai").should()}var l=function(e,r){this.x=e,this.y=r};l.add=function(s,i){if(e.versions){var g=s.toKey();g.compressed=!1;var p=i.toKey();p.compressed=!1;var w=o.addUncompressed(g.public,p.public),b=new o;return b.compressed=!1,b.public=w,b.compressed=!0,l.fromKey(b)}var m=d("secp256k1"),v=s.x.toBuffer({size:32}).toString("hex"),a=new c(v,16),y=s.y.toBuffer({size:32}).toString("hex"),B=new c(y,16),h=new f(m.getCurve().getQ(),a),C=new f(m.getCurve().getQ(),B),x=new u(m.getCurve(),h,C),z=i.x.toBuffer({size:32}).toString("hex"),q=new c(z,16),K=i.y.toBuffer({size:32}).toString("hex"),j=new c(K,16),P=new f(m.getCurve().getQ(),q),E=new f(m.getCurve().getQ(),j),S=new u(m.getCurve(),P,E),A=x.add(S),Q=new l,U=new r(A.getX().toBigInteger().toByteArrayUnsigned());Q.x=n.fromBuffer(U,{size:U.length}),t(U.length<=32);var F=new r(A.getY().toBigInteger().toByteArrayUnsigned());return t(F.length<=32),Q.y=n.fromBuffer(F,{size:F.length}),Q},l.fromKey=function(s){if(e.versions){var t=new l,u=new r(s.public),f=new o;return f.compressed=s.compressed,f.public=u,f.compressed=!1,t.x=n.fromBuffer(f.public.slice(1,33),{size:32}),t.y=n.fromBuffer(f.public.slice(33,65),{size:32}),t}var t=new l,u=new r(s.public),f=new i;return f.setCompressed(s.compressed),f.setPub(o.bufferToArray(u)),f.setCompressed(!1),t.x=n.fromBuffer(new r(f.getPub()).slice(1,33),{size:32}),t.y=n.fromBuffer(new r(f.getPub()).slice(33,65),{size:32}),t},l.prototype.toKey=function(){if(e.versions){var s=this.x.toBuffer({size:32}),n=this.y.toBuffer({size:32}),t=new o;t.compressed=!1;var u=new r([4]);return t.public=r.concat([u,s,n]),t.compressed=!0,t}var s=this.x.toBuffer({size:32}),n=this.y.toBuffer({size:32}),t=new i;t.setCompressed(!1);var u=new r([4]),f=r.concat([u,s,n]);t.setPub(o.bufferToArray(f)),t.setCompressed(!0);var d=new o;return d.public=new r(t.getPub()),d},module.exports=require("soop")(l)}).call(this,require("/Users/ryanxcharles/dev/bitcore/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),require("buffer").Buffer);
+},{"./Key":"Af1Gom","./browser/vendor-bundle.js":53,"/Users/ryanxcharles/dev/bitcore/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":91,"assert":72,"bignum":"J4nwo8","buffer":75,"chai":112,"soop":146}],"./PrivateKey":[function(require,module,exports){
+module.exports=require('+A60Qr');
+},{}],"+A60Qr":[function(require,module,exports){
+(function(t){function e(t,i,r){e.super(this,arguments),void 0!==r&&this.compressed(r)}var i=require("soop").imports(),r=i.parent||require("./util/VersionedData"),n=i.networks||require("./networks");e.parent=r,r.applyEncodingsTo(e),e.prototype.validate=function(){if(this.doAsBinary(function(){if(e.super(this,"validate",arguments),this.data.length<32||this.data.length>33&&!this.compressed()||34==this.data.length&&1!=this.data[33]||this.data.length>34)throw new Error("invalid data length")}),"undefined"==typeof this.network())throw new Error("invalid network")},e.prototype.payload=function(t){if(t)return this.doAsBinary(function(){t.copy(this.data,1)}),t;var e=this.as("binary");return 34==e.length?e.slice(1,33):33==e.length?e.slice(1):void 0},e.prototype.compressed=function(e){if(void 0===e){var i=34,r=this.as("binary");if(r.length==i&&1==r[i-1])return!0;if(r.length==i-1)return!1;throw new Error("invalid private key")}this.doAsBinary(function(){var i=34;if(e){var r=new t(i);this.data.copy(r),this.data=r,this.data[i-1]=1}else this.data=this.data.slice(0,i-1)})},e.prototype.network=function(){var t,e=this.version(),i=n.livenet,r=n.testnet;return e===i.privKeyVersion?t=i:e===r.privKeyVersion&&(t=r),t},module.exports=require("soop")(e)}).call(this,require("buffer").Buffer);
+},{"./networks":"ULNIu2","./util/VersionedData":"QLzNQg","buffer":75,"soop":146}],"Svwrvc":[function(require,module,exports){
+(function(t){function e(t){t=t||{},this.host=t.host||"127.0.0.1",this.port=t.port||8332,this.user=t.user||"user",this.pass=t.pass||"pass",this.protocol="http"==t.protocol?n:i,this.batchedCalls=null,this.disableAgent=t.disableAgent||!1}function r(t,e,r){function s(t,e){return function(){var s=arguments.length-1;if(this.batchedCalls)var s=arguments.length;for(var o=0;s>o;o++)e[o]&&(arguments[o]=e[o](arguments[o]));this.batchedCalls?this.batchedCalls.push({jsonrpc:"2.0",method:t,params:c(arguments)}):r.call(this,{method:t,params:c(arguments,0,arguments.length-1)},arguments[arguments.length-1])}}var o={str:function(t){return t.toString()},"int":function(t){return parseFloat(t)},"float":function(t){return parseFloat(t)},bool:function(t){return t===!0||"1"==t||"true"==t||"true"==t.toString().toLowerCase()}};for(var n in e)if(e.hasOwnProperty(n)){for(var i=e[n].split(" "),a=0;a=OP_1&&OP_16>=t}function prefixSize(t){return OP_PUSHDATA1>t?1:255>=t?2:65535>=t?3:5}function encodeLen(t){var r=void 0;return OP_PUSHDATA1>t?(r=new Buffer(1),r.writeUInt8(t,0)):255>=t?(r=new Buffer(2),r.writeUInt8(OP_PUSHDATA1,0),r.writeUInt8(t,1)):65535>=t?(r=new Buffer(3),r.writeUInt8(OP_PUSHDATA2,0),r.writeUInt16LE(t,1)):(r=new Buffer(5),r.writeUInt8(OP_PUSHDATA4,0),r.writeUInt32LE(t,1)),r}var imports=require("soop").imports(),config=imports.config||require("./config"),log=imports.log||require("./util/log"),Opcode=imports.Opcode||require("./Opcode"),buffertools=imports.buffertools||require("buffertools");for(var i in Opcode.map)eval(i+" = "+Opcode.map[i]+";");var util=imports.util||require("./util/util"),Parser=imports.Parser||require("./util/BinaryParser"),Put=imports.Put||require("bufferput"),TX_UNKNOWN=0,TX_PUBKEY=1,TX_PUBKEYHASH=2,TX_MULTISIG=3,TX_SCRIPTHASH=4,TX_TYPES=["unknown","pubkey","pubkeyhash","multisig","scripthash"];Script.TX_UNKNOWN=TX_UNKNOWN,Script.TX_PUBKEY=TX_PUBKEY,Script.TX_PUBKEYHASH=TX_PUBKEYHASH,Script.TX_MULTISIG=TX_MULTISIG,Script.TX_SCRIPTHASH=TX_SCRIPTHASH,Script.prototype.parse=function(){this.chunks=[];for(var t=new Parser(this.buffer);!t.eof();){var r,e,i=t.word8();i>0&&OP_PUSHDATA1>i?this.chunks.push(t.buffer(i)):i===OP_PUSHDATA1?(r=t.word8(),e=t.buffer(r),this.chunks.push(e)):i===OP_PUSHDATA2?(r=t.word16le(),e=t.buffer(r),this.chunks.push(e)):i===OP_PUSHDATA4?(r=t.word32le(),e=t.buffer(r),this.chunks.push(e)):this.chunks.push(i)}},Script.prototype.isPushOnly=function(){for(var t=0;tOP_16)return!1}return!0},Script.prototype.isP2SH=function(){return 3==this.chunks.length&&this.chunks[0]==OP_HASH160&&Buffer.isBuffer(this.chunks[1])&&20==this.chunks[1].length&&this.chunks[2]==OP_EQUAL},Script.prototype.isPubkey=function(){return 2==this.chunks.length&&Buffer.isBuffer(this.chunks[0])&&this.chunks[1]==OP_CHECKSIG},Script.prototype.isPubkeyHash=function(){return 5==this.chunks.length&&this.chunks[0]==OP_DUP&&this.chunks[1]==OP_HASH160&&Buffer.isBuffer(this.chunks[2])&&20==this.chunks[2].length&&this.chunks[3]==OP_EQUALVERIFY&&this.chunks[4]==OP_CHECKSIG},Script.prototype.isMultiSig=function(){return this.chunks.length>3&&isSmallIntOp(this.chunks[0])&&isSmallIntOp(this.chunks[this.chunks.length-2])&&this.chunks[this.chunks.length-1]==OP_CHECKMULTISIG},Script.prototype.finishedMultiSig=function(){for(var t=0,r=0;ri;i++){var u=this.chunks[i];if(i>0&&(e+=" "),e+=Buffer.isBuffer(u)?"0x"+util.formatBuffer(u,t?null:0):Opcode.reverseMap[u],r&&i>r){e+=" ...";break}}return e},Script.prototype.toString=function(t,r){var e="