2014-04-16 17:50:10 -03:00
'use strict' ;
2014-09-03 01:25:08 -03:00
var preconditions = require ( 'preconditions' ) . singleton ( ) ;
2014-04-16 17:50:10 -03:00
2014-10-25 19:57:12 -03:00
var _ = require ( 'lodash' ) ;
2014-10-24 12:24:44 -03:00
var bitcore = require ( 'bitcore' ) ;
2014-10-10 12:02:46 -03:00
var log = require ( '../log' ) ;
2014-10-24 12:24:44 -03:00
var async = require ( 'async' ) ;
2014-10-27 17:23:01 -03:00
var cryptoUtil = require ( '../util/crypto' ) ;
2014-10-10 12:02:46 -03:00
var version = require ( '../../version' ) . version ;
2014-08-01 01:09:46 -03:00
var TxProposals = require ( './TxProposals' ) ;
2014-04-16 17:50:10 -03:00
var PublicKeyRing = require ( './PublicKeyRing' ) ;
var PrivateKey = require ( './PrivateKey' ) ;
var Wallet = require ( './Wallet' ) ;
2014-09-03 01:25:08 -03:00
var PluginManager = require ( './PluginManager' ) ;
2014-09-30 20:12:02 -03:00
var Async = module . exports . Async = require ( './Async' ) ;
2014-06-15 20:55:23 -07:00
2014-09-04 01:33:12 -03:00
/ * *
* @ desc
2014-09-26 05:00:43 -03:00
* Identity - stores the state for a wallet in creation
2014-09-04 01:33:12 -03:00
*
2014-10-24 12:24:44 -03:00
* @ param { Object } opts - configuration for this wallet
* @ param { string } opts . fullName
* @ param { string } opts . email
* @ param { string } opts . password
* @ param { string } opts . storage
* @ param { string } opts . pluginManager
* @ param { Object } opts . walletDefaults
* @ param { string } opts . version
* @ param { Object } opts . wallets
* @ param { Object } opts . network
* @ param { string } opts . network . testnet
* @ param { string } opts . network . livenet
2014-09-08 15:42:55 -03:00
* @ constructor
2014-04-16 17:50:10 -03:00
* /
2014-10-24 12:24:44 -03:00
function Identity ( opts ) {
2014-09-28 18:38:06 -03:00
preconditions . checkArgument ( opts ) ;
2014-06-15 20:55:23 -07:00
2014-10-22 00:14:48 -03:00
opts = _ . extend ( { } , opts ) ;
2014-10-07 18:33:55 -03:00
this . networkOpts = {
'livenet' : opts . network . livenet ,
'testnet' : opts . network . testnet ,
2014-09-10 11:12:28 -07:00
} ;
2014-10-07 18:33:55 -03:00
this . blockchainOpts = {
'livenet' : opts . network . livenet ,
'testnet' : opts . network . testnet ,
2014-09-10 11:12:28 -07:00
} ;
2014-04-16 17:50:10 -03:00
2014-10-24 12:24:44 -03:00
this . fullName = opts . fullName || opts . email ;
this . email = opts . email ;
this . password = opts . password ;
2014-09-28 18:38:06 -03:00
2014-10-24 12:24:44 -03:00
this . storage = opts . storage || opts . pluginManager . get ( 'DB' ) ;
this . storage . setCredentials ( this . email , this . password , { } ) ;
2014-09-27 18:53:34 -03:00
2014-10-24 12:24:44 -03:00
this . walletDefaults = opts . walletDefaults || { } ;
this . version = opts . version || version ;
2014-09-29 12:11:10 -03:00
2014-10-24 12:24:44 -03:00
this . wallets = opts . wallets || { } ;
2014-09-29 12:11:10 -03:00
} ;
2014-10-29 16:21:44 -03:00
Identity . getStoragePrefix = function ( ) {
return 'profile::' ;
} ;
2014-10-24 12:24:44 -03:00
Identity . getKeyForEmail = function ( email ) {
2014-10-29 16:21:44 -03:00
return Identity . getStoragePrefix ( ) + bitcore . util . sha256ripe160 ( email ) . toString ( 'hex' ) ;
2014-09-29 19:58:00 -03:00
} ;
2014-10-24 12:24:44 -03:00
Identity . prototype . getId = function ( ) {
return Identity . getKeyForEmail ( this . email ) ;
2014-10-01 08:35:17 -03:00
} ;
2014-10-24 12:24:44 -03:00
Identity . prototype . getName = function ( ) {
return this . fullName || this . email ;
2014-10-07 18:33:55 -03:00
} ;
2014-10-24 12:24:44 -03:00
/ * *
* Creates an Identity
*
* @ param opts
* @ param cb
* @ return { undefined }
* /
2014-10-30 18:08:50 -03:00
Identity . create = function ( opts , cb ) {
2014-10-24 12:24:44 -03:00
opts = _ . extend ( { } , opts ) ;
2014-10-11 08:43:51 -03:00
2014-10-30 18:08:50 -03:00
var iden = new Identity ( opts ) ;
iden . store ( opts , function ( err ) {
if ( err ) return cb ( err ) ;
return cb ( null , iden ) ;
} ) ;
2014-10-11 08:43:51 -03:00
} ;
2014-10-01 08:35:17 -03:00
2014-10-11 14:33:45 -03:00
/ * *
2014-10-24 12:24:44 -03:00
* Open an Identity from the given storage
2014-10-11 14:33:45 -03:00
*
2014-10-24 12:24:44 -03:00
* @ param { Object } opts
* @ param { Object } opts . storage
2014-10-25 20:10:54 -03:00
* @ param { string } opts . email
* @ param { string } opts . password
2014-10-24 12:24:44 -03:00
* @ param { Function } cb
2014-10-11 14:33:45 -03:00
* /
2014-10-25 20:10:54 -03:00
Identity . open = function ( opts , cb ) {
2014-10-24 12:24:44 -03:00
var storage = opts . storage || opts . pluginManager . get ( 'DB' ) ;
2014-10-25 20:10:54 -03:00
storage . setCredentials ( opts . email , opts . password , opts ) ;
storage . getItem ( Identity . getKeyForEmail ( opts . email ) , function ( err , data ) {
2014-10-24 12:24:44 -03:00
if ( err ) {
return cb ( err ) ;
}
return Identity . createFromPartialJson ( data , opts , cb ) ;
2014-10-24 09:36:28 -03:00
} ) ;
2014-10-11 14:33:45 -03:00
} ;
2014-09-28 18:38:06 -03:00
/ * *
2014-10-24 12:24:44 -03:00
* Creates an Identity , retrieves all Wallets remotely , and activates network
2014-09-28 18:38:06 -03:00
*
2014-10-24 12:24:44 -03:00
* @ param { string } jsonString - a string containing a json object with options to rebuild the identity
* @ param { Object } opts
* @ param { Function } cb
2014-09-28 18:38:06 -03:00
* /
2014-10-24 12:24:44 -03:00
Identity . createFromPartialJson = function ( jsonString , opts , callback ) {
var exported ;
try {
exported = JSON . parse ( jsonString ) ;
} catch ( e ) {
return callback ( 'Invalid JSON' ) ;
}
var identity = new Identity ( _ . extend ( opts , exported ) ) ;
async . map ( exported . walletIds , function ( walletId , callback ) {
2014-10-27 14:53:40 -03:00
identity . retrieveWalletFromStorage ( walletId , { } , function ( error , wallet ) {
2014-10-24 12:24:44 -03:00
if ( ! error ) {
identity . wallets [ wallet . getId ( ) ] = wallet ;
2014-10-27 15:00:08 -03:00
identity . bindWallet ( wallet ) ;
2014-10-24 12:24:44 -03:00
wallet . netStart ( ) ;
2014-10-23 17:26:32 -03:00
}
2014-10-24 12:24:44 -03:00
callback ( error , wallet ) ;
2014-09-30 16:04:17 -03:00
} ) ;
2014-10-24 12:24:44 -03:00
} , function ( err ) {
return callback ( err , identity ) ;
2014-09-28 18:38:06 -03:00
} ) ;
} ;
/ * *
2014-10-24 12:24:44 -03:00
* @ param { string } walletId
2014-10-27 14:53:40 -03:00
* @ param { } opts
* opts . importWallet
2014-10-24 12:24:44 -03:00
* @ param { Function } callback
2014-09-28 18:38:06 -03:00
* /
2014-10-27 14:53:40 -03:00
Identity . prototype . retrieveWalletFromStorage = function ( walletId , opts , callback ) {
2014-10-24 12:24:44 -03:00
var self = this ;
2014-10-27 14:53:40 -03:00
var importFunction = opts . importWallet || Wallet . fromUntrustedObj ;
2014-10-24 12:24:44 -03:00
this . storage . getItem ( Wallet . getStorageKey ( walletId ) , function ( error , walletData ) {
if ( error ) {
return callback ( error ) ;
}
try {
log . debug ( '## OPENING Wallet: ' + walletId ) ;
if ( _ . isString ( walletData ) ) {
walletData = JSON . parse ( walletData ) ;
}
var readOpts = {
networkOpts : self . networkOpts ,
blockchainOpts : self . blockchainOpts ,
skipFields : [ ]
} ;
2014-10-27 14:53:40 -03:00
return callback ( null , importFunction ( walletData , readOpts ) ) ;
2014-09-28 18:38:06 -03:00
2014-10-24 12:24:44 -03:00
} catch ( e ) {
2014-09-30 08:09:39 -03:00
2014-10-24 12:24:44 -03:00
log . debug ( "ERROR: " , e . message ) ;
if ( e && e . message && e . message . indexOf ( 'MISSOPTS' ) !== - 1 ) {
return callback ( new Error ( 'WERROR: Could not read: ' + walletId + ': ' + e . message ) ) ;
} else {
return callback ( e ) ;
2014-10-22 00:14:48 -03:00
}
}
2014-09-28 18:38:06 -03:00
} ) ;
} ;
2014-10-24 12:24:44 -03:00
/ * *
* @ param { Wallet } wallet
* @ param { Function } cb
* /
Identity . prototype . storeWallet = function ( wallet , cb ) {
2014-10-27 11:57:13 -03:00
preconditions . checkArgument ( wallet && _ . isObject ( wallet ) ) ;
2014-10-24 09:36:28 -03:00
2014-10-24 12:24:44 -03:00
var val = wallet . toObj ( ) ;
var key = wallet . getStorageKey ( ) ;
2014-10-31 15:49:56 -03:00
log . debug ( 'Storing wallet:' + wallet . getName ( ) ) ;
2014-10-24 09:36:28 -03:00
2014-10-24 12:24:44 -03:00
this . storage . setItem ( key , val , function ( err ) {
if ( err ) {
2014-10-27 11:57:13 -03:00
log . debug ( 'Wallet:' + wallet . getName ( ) + ' couldnt be stored' ) ;
2014-10-24 09:36:28 -03:00
}
2014-10-27 14:53:40 -03:00
if ( cb )
return cb ( err ) ;
2014-10-24 09:36:28 -03:00
} ) ;
} ;
2014-09-28 18:38:06 -03:00
2014-10-31 15:49:56 -03:00
/ * *
* @ param { Identity } identity
* @ param { Wallet } wallet
* @ param { Function } cb
* /
Identity . storeWalletDebounced = _ . debounce ( function ( identity , wallet , cb ) {
identity . storeWallet ( wallet , cb ) ;
} , 3000 ) ;
2014-10-24 12:24:44 -03:00
Identity . prototype . toObj = function ( ) {
2014-10-27 10:49:25 -03:00
return _ . extend ( {
walletIds : _ . keys ( this . wallets )
} ,
_ . pick ( this , 'version' , 'fullName' , 'password' , 'email' ) ) ;
2014-10-24 08:37:40 -03:00
} ;
2014-10-28 00:31:30 -03:00
Identity . prototype . exportEncryptedWithWalletInfo = function ( opts ) {
var crypto = opts . cryptoUtil || cryptoUtil ;
var key = crypto . kdf ( this . password ) ;
2014-10-28 15:21:29 -03:00
return crypto . encrypt ( key , this . exportWithWalletInfo ( opts ) ) ;
2014-10-28 00:31:30 -03:00
} ;
Identity . prototype . exportWithWalletInfo = function ( opts ) {
2014-10-27 10:49:25 -03:00
return _ . extend ( {
wallets : _ . map ( this . wallets , function ( wallet ) {
return wallet . toObj ( ) ;
} )
} ,
2014-10-28 00:31:30 -03:00
_ . pick ( this , 'version' , 'fullName' , 'password' , 'email' )
) ;
2014-10-24 12:24:44 -03:00
} ;
2014-10-24 08:37:40 -03:00
2014-09-29 10:18:47 -03:00
/ * *
2014-10-24 12:24:44 -03:00
* @ param { Object } opts
* @ param { Function } cb
2014-09-29 10:18:47 -03:00
* /
2014-09-28 18:38:06 -03:00
Identity . prototype . store = function ( opts , cb ) {
2014-10-29 23:23:16 -03:00
log . debug ( 'Storing profile' ) ;
2014-09-28 20:50:37 -03:00
var self = this ;
2014-10-27 17:23:01 -03:00
opts = opts || { } ;
2014-10-28 17:00:01 -03:00
var storeFunction = opts . failIfExists ? self . storage . createItem : self . storage . setItem ;
storeFunction . call ( self . storage , this . getId ( ) , this . toObj ( ) , function ( err ) {
2014-09-28 21:22:53 -03:00
if ( err ) return cb ( err ) ;
2014-10-27 14:53:40 -03:00
2014-10-28 09:57:26 -03:00
if ( opts . noWallets )
2014-10-27 14:53:40 -03:00
return cb ( ) ;
2014-10-24 12:24:44 -03:00
async . map ( self . wallets , self . storeWallet , cb ) ;
2014-09-28 20:50:37 -03:00
} ) ;
2014-09-28 18:38:06 -03:00
} ;
2014-10-05 15:59:41 -03:00
Identity . prototype . _cleanUp = function ( ) {
2014-10-07 18:33:55 -03:00
// NOP
2014-10-05 15:59:41 -03:00
} ;
2014-09-30 21:16:46 -03:00
/ * *
* @ desc Closes the wallet and disconnects all services
* /
Identity . prototype . close = function ( cb ) {
2014-10-24 12:24:44 -03:00
async . map ( this . wallets , function ( wallet , callback ) {
wallet . close ( callback ) ;
} , cb ) ;
2014-09-30 21:16:46 -03:00
} ;
2014-09-04 01:33:12 -03:00
/ * *
2014-10-28 00:31:30 -03:00
* @ desc Imports a wallet from an encrypted string
* @ param { string } cypherText - the encrypted object
2014-09-18 18:29:00 -03:00
* @ param { string } passphrase - passphrase to decrypt it
2014-10-27 17:23:01 -03:00
* @ param { string [ ] } opts . skipFields - fields to ignore when importing
2014-10-28 18:06:32 -03:00
* @ param { string [ ] } opts . salt -
* @ param { string [ ] } opts . iterations -
2014-10-27 17:23:01 -03:00
* @ param { string [ ] } opts . importFunction - for stubbing
2014-09-04 01:33:12 -03:00
* @ return { Wallet }
* /
2014-10-28 00:31:30 -03:00
Identity . prototype . importEncryptedWallet = function ( cypherText , password , opts , cb ) {
var crypto = opts . cryptoUtil || cryptoUtil ;
// TODO set iter and salt using config.js
var key = crypto . kdf ( password ) ;
var obj = crypto . decrypt ( key , cypherText ) ;
if ( ! obj ) return cb ( new Error ( 'Could not decrypt' ) ) ;
try {
obj = JSON . parse ( obj ) ;
} catch ( e ) {
return cb ( new Error ( 'Could not decrypt' ) ) ;
}
return this . importWalletFromObj ( obj , opts , cb )
} ;
Identity . prototype . importWalletFromObj = function ( obj , opts , cb ) {
2014-10-16 17:39:22 -03:00
var self = this ;
2014-09-29 16:55:45 -03:00
preconditions . checkArgument ( cb ) ;
2014-10-27 17:23:01 -03:00
var importFunction = opts . importWallet || Wallet . fromUntrustedObj ;
2014-09-29 11:35:04 -03:00
2014-10-16 17:39:22 -03:00
var readOpts = {
networkOpts : this . networkOpts ,
blockchainOpts : this . blockchainOpts ,
2014-10-27 17:23:01 -03:00
skipFields : opts . skipFields ,
2014-10-16 17:39:22 -03:00
} ;
2014-10-27 17:23:01 -03:00
var w = importFunction ( obj , readOpts ) ;
if ( ! w ) return cb ( new Error ( 'Could not decrypt' ) ) ;
2014-09-29 11:35:04 -03:00
this . _checkVersion ( w . version ) ;
2014-09-29 16:55:45 -03:00
this . addWallet ( w , function ( err ) {
2014-10-16 17:39:22 -03:00
if ( err ) return cb ( err , null ) ;
2014-10-24 08:37:40 -03:00
self . wallets [ w . getId ( ) ] = w ;
2014-10-27 14:53:40 -03:00
self . bindWallet ( w ) ;
2014-10-16 17:39:22 -03:00
self . store ( null , function ( err ) {
return cb ( err , w ) ;
} ) ;
2014-09-29 11:35:04 -03:00
} ) ;
2014-05-01 18:32:22 -03:00
} ;
2014-10-05 15:59:41 -03:00
2014-10-24 12:24:44 -03:00
/ * *
* @ param { Wallet } wallet
* @ param { Function } cb
* /
Identity . prototype . closeWallet = function ( wallet , cb ) {
preconditions . checkState ( wallet , 'Wallet not found' ) ;
2014-10-05 15:59:41 -03:00
2014-10-24 12:24:44 -03:00
wallet . close ( function ( err ) {
2014-10-24 08:37:40 -03:00
delete self . wallets [ wid ] ;
2014-10-05 15:59:41 -03:00
return cb ( err ) ;
} ) ;
} ;
2014-10-28 00:31:30 -03:00
Identity . importFromEncryptedFullJson = function ( str , password , opts , cb ) {
var crypto = opts . cryptoUtil || cryptoUtil ;
var key = crypto . kdf ( password ) ;
return Identity . importFromFullJson ( crypto . decript ( key , str ) ) ;
} ;
2014-10-24 12:24:44 -03:00
Identity . importFromFullJson = function ( str , password , opts , cb ) {
2014-10-21 09:57:54 -03:00
preconditions . checkArgument ( str ) ;
2014-10-22 00:14:48 -03:00
var json ;
try {
json = JSON . parse ( str ) ;
} catch ( e ) {
return cb ( 'Unable to retrieve json from string' , str ) ;
}
2014-10-21 09:57:54 -03:00
2014-10-21 12:03:09 -03:00
if ( ! _ . isNumber ( json . iterations ) )
2014-10-24 08:37:40 -03:00
return cb ( 'BADSTR: Missing iterations' ) ;
2014-10-21 09:57:54 -03:00
2014-10-24 12:24:44 -03:00
var email = json . email ;
var iden = new Identity ( email , password , opts ) ;
2014-10-21 09:57:54 -03:00
json . wallets = json . wallets || { } ;
2014-10-24 12:24:44 -03:00
async . map ( json . wallets , function ( walletData , callback ) {
2014-10-28 00:31:30 -03:00
iden . importEncryptedWallet ( wstr , password , opts , function ( err , w ) {
2014-10-24 12:24:44 -03:00
if ( err ) return callback ( err ) ;
2014-10-21 09:57:54 -03:00
log . debug ( 'Wallet ' + w . getId ( ) + ' imported' ) ;
2014-10-24 12:24:44 -03:00
callback ( ) ;
} ) ;
} , function ( err , results ) {
2014-10-29 23:23:16 -03:00
if ( err ) return cb ( err ) ;
2014-10-28 17:00:01 -03:00
iden . store ( null , function ( err ) {
2014-10-29 23:23:16 -03:00
return cb ( err , iden ) ;
2014-10-24 12:24:44 -03:00
} ) ;
2014-10-15 16:24:21 -03:00
} ) ;
2014-10-15 15:02:14 -03:00
} ;
2014-10-24 08:37:40 -03:00
Identity . prototype . bindWallet = function ( w ) {
var self = this ;
self . wallets [ w . getId ( ) ] = w ;
log . debug ( 'Binding wallet ' + w . getName ( ) ) ;
2014-10-27 13:13:08 -03:00
w . on ( 'txProposalsUpdated' , function ( ) {
2014-10-31 15:49:56 -03:00
Identity . storeWalletDebounced ( self , w ) ;
2014-10-27 13:13:08 -03:00
} ) ;
w . on ( 'newAddresses' , function ( ) {
2014-10-31 15:49:56 -03:00
Identity . storeWalletDebounced ( self , w ) ;
2014-10-27 13:13:08 -03:00
} ) ;
w . on ( 'settingsUpdated' , function ( ) {
2014-10-31 15:49:56 -03:00
Identity . storeWalletDebounced ( self , w ) ;
2014-10-27 13:13:08 -03:00
} ) ;
w . on ( 'txProposalEvent' , function ( ) {
2014-10-31 15:49:56 -03:00
Identity . storeWalletDebounced ( self , w ) ;
2014-10-27 13:13:08 -03:00
} ) ;
w . on ( 'ready' , function ( ) {
2014-10-30 18:08:50 -03:00
self . store ( {
noWallets : true
} , function ( ) {
2014-10-31 15:49:56 -03:00
Identity . storeWalletDebounced ( self , w ) ;
2014-10-29 23:23:16 -03:00
} ) ;
2014-10-27 13:13:08 -03:00
} ) ;
w . on ( 'addressBookUpdated' , function ( ) {
2014-10-31 15:49:56 -03:00
Identity . storeWalletDebounced ( self , w ) ;
2014-10-24 08:37:40 -03:00
} ) ;
2014-10-29 14:53:52 -03:00
w . on ( 'publicKeyRingUpdated' , function ( ) {
2014-10-31 15:49:56 -03:00
Identity . storeWalletDebounced ( self , w ) ;
2014-10-29 14:53:52 -03:00
} ) ;
2014-10-24 08:37:40 -03:00
} ;
2014-09-04 01:33:12 -03:00
/ * *
2014-09-14 13:52:43 -03:00
* @ desc This method prepares options for a new Wallet
2014-09-04 01:33:12 -03:00
*
* @ param { Object } opts
* @ param { string } opts . id
* @ param { PrivateKey = } opts . privateKey
* @ param { string = } opts . privateKeyHex
* @ param { number } opts . requiredCopayers
* @ param { number } opts . totalCopayers
* @ param { PublicKeyRing = } opts . publicKeyRing
* @ param { string } opts . nickname
2014-09-30 21:16:46 -03:00
* @ param { string } opts . password
2014-10-28 17:00:01 -03:00
* @ param { boolean } opts . spendUnconfirmed this . walletDefaults . spendUnconfirmed
* @ param { number } opts . reconnectDelay time in milliseconds
2014-09-04 01:33:12 -03:00
* @ param { number = } opts . version
2014-09-03 15:43:27 -03:00
* @ param { callback } opts . version
2014-09-04 01:33:12 -03:00
* @ return { Wallet }
* /
2014-09-27 18:56:25 -03:00
Identity . prototype . createWallet = function ( opts , cb ) {
2014-09-08 10:46:57 -03:00
preconditions . checkArgument ( cb ) ;
2014-09-10 13:56:49 -07:00
opts = opts || { } ;
opts . networkName = opts . networkName || 'testnet' ;
2014-09-10 11:12:28 -07:00
2014-09-01 12:35:40 -03:00
log . debug ( '### CREATING NEW WALLET.' + ( opts . id ? ' USING ID: ' + opts . id : ' NEW ID' ) + ( opts . privateKey ? ' USING PrivateKey: ' + opts . privateKey . getId ( ) : ' NEW PrivateKey' ) ) ;
2014-04-16 17:50:10 -03:00
2014-08-21 14:54:36 -04:00
var privOpts = {
2014-09-09 15:30:49 -07:00
networkName : opts . networkName ,
2014-08-21 14:54:36 -04:00
} ;
2014-09-04 16:13:30 -03:00
if ( opts . privateKeyHex && opts . privateKeyHex . length > 1 ) {
2014-08-21 14:54:36 -04:00
privOpts . extendedPrivateKeyString = opts . privateKeyHex ;
}
opts . privateKey = opts . privateKey || new PrivateKey ( privOpts ) ;
2014-04-20 12:41:28 -03:00
2014-04-16 17:50:10 -03:00
var requiredCopayers = opts . requiredCopayers || this . walletDefaults . requiredCopayers ;
2014-06-16 15:51:19 -03:00
var totalCopayers = opts . totalCopayers || this . walletDefaults . totalCopayers ;
2014-08-14 18:46:42 -04:00
opts . lockTimeoutMin = this . walletDefaults . idleDurationMin ;
2014-04-16 17:50:10 -03:00
opts . publicKeyRing = opts . publicKeyRing || new PublicKeyRing ( {
2014-09-09 15:30:49 -07:00
networkName : opts . networkName ,
2014-04-16 17:50:10 -03:00
requiredCopayers : requiredCopayers ,
totalCopayers : totalCopayers ,
} ) ;
2014-05-28 16:10:05 -03:00
opts . publicKeyRing . addCopayer (
opts . privateKey . deriveBIP45Branch ( ) . extendedPublicKeyString ( ) ,
2014-10-24 12:24:44 -03:00
opts . nickname || this . getName ( )
2014-08-01 11:24:16 -03:00
) ;
2014-09-01 12:35:40 -03:00
log . debug ( '\t### PublicKeyRing Initialized' ) ;
2014-04-16 17:50:10 -03:00
2014-08-05 16:42:51 -03:00
opts . txProposals = opts . txProposals || new TxProposals ( {
2014-09-09 15:30:49 -07:00
networkName : opts . networkName ,
2014-04-16 17:50:10 -03:00
} ) ;
2014-10-28 09:57:26 -03:00
var walletClass = opts . walletClass || Wallet ;
2014-10-27 13:13:08 -03:00
2014-09-01 12:35:40 -03:00
log . debug ( '\t### TxProposals Initialized' ) ;
2014-04-16 17:50:10 -03:00
2014-05-01 10:03:58 -03:00
2014-10-07 18:33:55 -03:00
opts . networkOpts = this . networkOpts ;
opts . blockchainOpts = this . blockchainOpts ;
2014-04-16 20:58:57 -03:00
2014-04-16 17:50:10 -03:00
opts . spendUnconfirmed = opts . spendUnconfirmed || this . walletDefaults . spendUnconfirmed ;
2014-06-16 15:51:19 -03:00
opts . reconnectDelay = opts . reconnectDelay || this . walletDefaults . reconnectDelay ;
2014-04-16 17:50:10 -03:00
opts . requiredCopayers = requiredCopayers ;
2014-06-16 15:51:19 -03:00
opts . totalCopayers = totalCopayers ;
opts . version = opts . version || this . version ;
2014-08-14 18:46:42 -04:00
2014-09-29 06:31:04 -03:00
var self = this ;
2014-10-27 13:13:08 -03:00
var w = new walletClass ( opts ) ;
2014-09-29 11:35:04 -03:00
this . addWallet ( w , function ( err ) {
2014-09-14 13:52:43 -03:00
if ( err ) return cb ( err ) ;
2014-10-24 08:37:40 -03:00
self . bindWallet ( w ) ;
2014-10-27 14:53:40 -03:00
w . netStart ( ) ;
2014-10-28 17:00:01 -03:00
return cb ( err , w ) ;
2014-10-22 00:14:48 -03:00
} ) ;
} ;
2014-09-29 11:35:04 -03:00
Identity . prototype . addWallet = function ( wallet , cb ) {
2014-09-29 12:11:10 -03:00
preconditions . checkArgument ( wallet ) ;
2014-09-29 16:55:45 -03:00
preconditions . checkArgument ( wallet . getId ) ;
2014-09-29 11:35:04 -03:00
preconditions . checkArgument ( cb ) ;
2014-10-24 12:24:44 -03:00
this . wallets [ wallet . getId ( ) ] = wallet ;
// TODO (eordano): Consider not saving automatically after this
this . storage . setItem ( wallet . getStorageKey ( ) , wallet . toObj ( ) , cb ) ;
2014-09-29 11:35:04 -03:00
} ;
2014-09-04 01:33:12 -03:00
/ * *
* @ desc Checks if a version is compatible with the current version
* @ param { string } inVersion - a version , with major , minor , and revision , period - separated ( x . y . z )
* @ throws { Error } if there ' s a major version difference
* /
2014-09-26 05:00:43 -03:00
Identity . prototype . _checkVersion = function ( inVersion ) {
2014-09-29 12:11:10 -03:00
if ( inVersion ) {
var thisV = this . version . split ( '.' ) ;
var thisV0 = parseInt ( thisV [ 0 ] ) ;
var inV = inVersion . split ( '.' ) ;
var inV0 = parseInt ( inV [ 0 ] ) ;
}
2014-05-14 21:02:01 -03:00
//We only check for major version differences
2014-06-16 15:51:19 -03:00
if ( thisV0 < inV0 ) {
2014-05-14 21:02:01 -03:00
throw new Error ( 'Major difference in software versions' +
2014-09-04 16:13:30 -03:00
'. Received:' + inVersion +
'. Current version:' + this . version +
'. Aborting.' ) ;
2014-05-14 21:02:01 -03:00
}
} ;
2014-10-24 12:24:44 -03:00
/ * *
* @ param { string } walletId
* @ returns { Wallet }
* /
Identity . prototype . getWalletById = function ( walletId ) {
return this . wallets [ walletId ] ;
2014-10-08 10:54:26 -03:00
} ;
2014-10-24 12:24:44 -03:00
/ * *
* @ returns { Wallet [ ] }
* /
2014-10-14 13:22:28 -03:00
Identity . prototype . listWallets = function ( ) {
2014-10-24 12:24:44 -03:00
return _ . values ( this . wallets ) ;
2014-04-24 16:35:52 -03:00
} ;
2014-04-16 17:50:10 -03:00
2014-09-04 01:33:12 -03:00
/ * *
2014-10-24 12:24:44 -03:00
* @ desc Deletes a wallet . This involves removing it from the storage instance
*
2014-09-04 01:33:12 -03:00
* @ param { string } walletId
* @ callback cb
2014-09-29 10:18:47 -03:00
* @ return { err }
2014-09-04 01:33:12 -03:00
* /
2014-09-28 18:38:06 -03:00
Identity . prototype . deleteWallet = function ( walletId , cb ) {
2014-09-23 16:01:02 -03:00
var self = this ;
2014-10-24 12:24:44 -03:00
delete this . wallets [ walletId ] ;
2014-10-27 10:49:25 -03:00
this . storage . removeItem ( Wallet . getStorageKey ( walletId ) , function ( err ) {
2014-10-24 12:24:44 -03:00
if ( err ) {
2014-09-13 10:25:13 -03:00
return cb ( err ) ;
2014-10-24 12:24:44 -03:00
}
2014-10-28 17:00:01 -03:00
self . store ( null , cb ) ;
2014-10-24 12:24:44 -03:00
} ) ;
2014-04-16 17:50:10 -03:00
} ;
2014-09-04 01:33:12 -03:00
/ * *
* @ desc Pass through to { @ link Wallet # secret }
* /
2014-09-26 05:00:43 -03:00
Identity . prototype . decodeSecret = function ( secret ) {
2014-05-14 14:24:24 -07:00
try {
return Wallet . decodeSecret ( secret ) ;
} catch ( e ) {
return false ;
}
2014-06-09 18:01:15 -03:00
} ;
2014-04-16 20:58:57 -03:00
2014-10-24 12:24:44 -03:00
Identity . prototype . getLastFocusedWallet = function ( ) {
2014-10-27 11:10:32 -03:00
if ( _ . keys ( this . wallets ) . length == 0 ) return ;
2014-10-24 12:24:44 -03:00
return _ . max ( this . wallets , function ( wallet ) {
2014-10-31 12:27:22 -03:00
return wallet . focusedTimestamp || 0 ;
2014-10-24 12:24:44 -03:00
} ) ;
} ;
2014-09-04 01:33:12 -03:00
/ * *
* @ callback walletCreationCallback
2014-09-08 15:42:55 -03:00
* @ param { ? } err - an error , if any , that happened during the wallet creation
2014-09-04 01:33:12 -03:00
* @ param { Wallet = } wallet - the wallet created
* /
2014-04-20 12:41:28 -03:00
2014-09-04 01:33:12 -03:00
/ * *
* @ desc Start the network functionality .
*
* Start up the Network instance and try to join a wallet defined by the
* parameter < tt > secret < / t t > u s i n g t h e p a r a m e t e r < t t > n i c k n a m e < / t t > . E n c o d e
* information locally using < tt > passphrase < / t t > . < t t > p r i v a t e H e x < / t t > i s t h e
* private extended master key . < tt > cb < / t t > h a s t w o p a r a m s : e r r o r a n d w a l l e t .
*
2014-09-14 13:52:43 -03:00
* @ param { object } opts
* @ param { string } opts . secret - the wallet secret
* @ param { string } opts . nickname - a nickname for the current user
* @ param { string } opts . privateHex - the private extended master key
2014-09-04 01:33:12 -03:00
* @ param { walletCreationCallback } cb - a callback
* /
2014-09-27 18:56:25 -03:00
Identity . prototype . joinWallet = function ( opts , cb ) {
2014-09-14 13:52:43 -03:00
preconditions . checkArgument ( opts ) ;
preconditions . checkArgument ( opts . secret ) ;
2014-09-08 10:46:57 -03:00
preconditions . checkArgument ( cb ) ;
2014-08-20 18:00:33 -04:00
var self = this ;
2014-09-14 13:52:43 -03:00
var decodedSecret = this . decodeSecret ( opts . secret ) ;
2014-09-10 14:01:10 -07:00
if ( ! decodedSecret || ! decodedSecret . networkName || ! decodedSecret . pubKey ) {
2014-09-10 11:12:28 -07:00
return cb ( 'badSecret' ) ;
}
2014-06-16 15:51:19 -03:00
2014-08-20 18:00:33 -04:00
var privOpts = {
2014-09-09 15:30:49 -07:00
networkName : decodedSecret . networkName ,
2014-08-20 18:00:33 -04:00
} ;
2014-09-14 13:52:43 -03:00
if ( opts . privateHex && opts . privateHex . length > 1 ) {
2014-10-04 11:30:08 -03:00
privOpts . extendedPrivateKeyString = opts . privateHex ;
2014-08-20 18:00:33 -04:00
}
2014-04-20 12:41:28 -03:00
//Create our PrivateK
2014-08-20 18:00:33 -04:00
var privateKey = new PrivateKey ( privOpts ) ;
2014-09-01 12:35:40 -03:00
log . debug ( '\t### PrivateKey Initialized' ) ;
2014-09-18 18:29:00 -03:00
var joinOpts = {
2014-04-24 23:13:55 -03:00
copayerId : privateKey . getId ( ) ,
2014-06-26 08:49:22 -07:00
privkey : privateKey . getIdPriv ( ) ,
2014-09-03 16:51:02 -03:00
key : privateKey . getIdKey ( ) ,
2014-09-09 15:30:49 -07:00
secretNumber : decodedSecret . secretNumber ,
2014-04-24 23:13:55 -03:00
} ;
2014-09-10 11:12:28 -07:00
2014-10-07 18:33:55 -03:00
2014-10-24 12:24:44 -03:00
var joinNetwork = opts . Async || new Async ( this . networkOpts [ decodedSecret . networkName ] ) ;
2014-07-08 15:25:12 -03:00
// This is a hack to reconize if the connection was rejected or the peer wasn't there.
var connectedOnce = false ;
2014-09-10 11:12:28 -07:00
joinNetwork . on ( 'connected' , function ( sender , data ) {
2014-07-08 15:25:12 -03:00
connectedOnce = true ;
} ) ;
2014-09-19 11:15:45 -03:00
joinNetwork . on ( 'connect_error' , function ( ) {
return cb ( 'connectionError' ) ;
} ) ;
2014-09-10 11:12:28 -07:00
joinNetwork . on ( 'serverError' , function ( ) {
2014-07-08 15:25:12 -03:00
return cb ( 'joinError' ) ;
} ) ;
2014-09-18 18:29:00 -03:00
joinNetwork . start ( joinOpts , function ( ) {
joinNetwork . greet ( decodedSecret . pubKey , joinOpts . secretNumber ) ;
2014-09-10 11:12:28 -07:00
joinNetwork . on ( 'data' , function ( sender , data ) {
2014-09-15 14:25:09 -03:00
if ( data . type === 'walletId' && data . opts ) {
2014-10-04 19:52:43 -03:00
if ( ! data . networkName || data . networkName !== decodedSecret . networkName ) {
2014-06-09 21:00:28 -03:00
return cb ( 'badNetwork' ) ;
}
2014-10-04 19:52:43 -03:00
data . opts . networkName = data . networkName ;
2014-06-09 21:00:28 -03:00
2014-09-23 15:24:57 -03:00
var walletOpts = _ . clone ( data . opts ) ;
2014-09-18 18:29:00 -03:00
walletOpts . id = data . walletId ;
2014-10-14 19:49:29 -03:00
walletOpts . network = joinNetwork ;
2014-09-18 18:29:00 -03:00
walletOpts . privateKey = privateKey ;
2014-10-24 12:24:44 -03:00
walletOpts . nickname = opts . nickname || self . getName ( ) ;
2014-10-05 15:59:41 -03:00
if ( opts . password )
walletOpts . password = opts . password ;
2014-09-18 18:29:00 -03:00
2014-09-28 18:38:06 -03:00
self . createWallet ( walletOpts , function ( err , w ) {
2014-09-18 18:29:00 -03:00
if ( w ) {
w . sendWalletReady ( decodedSecret . pubKey ) ;
2014-09-15 14:25:09 -03:00
} else {
2014-10-26 21:06:43 -03:00
if ( ! err ) {
err = 'walletFull' ;
}
2014-09-15 14:25:09 -03:00
}
2014-10-29 08:59:06 -03:00
if ( err )
return cb ( err ) ;
2014-10-28 17:00:01 -03:00
self . store ( {
noWallets : true
} , function ( err ) {
return cb ( err , w ) ;
} ) ;
2014-09-15 14:25:09 -03:00
} ) ;
2014-04-20 16:16:09 -03:00
}
2014-04-16 20:58:57 -03:00
} ) ;
} ) ;
} ;
2014-09-27 17:14:49 -03:00
2014-09-26 05:00:43 -03:00
module . exports = Identity ;