@ -11,13 +11,16 @@ log.debug = log.verbose;
var Bitcore = require ( 'bitcore' )
var WalletUtils = require ( '../walletutils' ) ;
var Verifier = require ( './verifier' ) ;
var ServerCompromisedError = require ( './servercompromisederror' )
var ServerCompromisedError = require ( './servercompromisederror' ) ;
var ClientError = require ( '../clienterror' ) ;
var BASE_URL = 'http://localhost:3001/copay/api' ;
var WALLET_CRITICAL_DATA = [ 'xPrivKey' , 'm' , 'publicKeyRing' , 'sharedEncryptingKey' ] ;
var WALLET_CRITICAL_DATA = [ 'xPrivKey' , 'm' , 'n' , ' publicKeyRing' , 'sharedEncryptingKey' ] ;
var WALLET_EXTRA_DATA = [ 'copayerId' , 'roPrivKey' , 'rwPrivKey' ] ;
var WALLET_AIRGAPPED_TOCOMPLETE = [ 'publicKeyRing' , 'm' , 'n' , 'sharedEncryptingKey' ] ;
function _ encryptMessage ( message , encryptingKey ) {
if ( ! message ) return null ;
return WalletUtils . encryptMessage ( message , encryptingKey ) ;
@ -52,13 +55,17 @@ function _parseError(body) {
} ;
}
}
var code = body . code || 'ERROR' ;
var message = body . error || 'There was an unknown error processing the request' ;
log . error ( code , message ) ;
return {
message : message ,
code : code
} ;
var ret ;
if ( body . code ) {
ret = new ClientError ( body . code , body . message ) ;
} else {
ret = {
code : 'ERROR' ,
error : body . error || 'There was an unknown error processing the request' ,
} ;
}
log . error ( ret ) ;
return ret ;
} ;
function _ signRequest ( method , url , args , privKey ) {
@ -66,6 +73,34 @@ function _signRequest(method, url, args, privKey) {
return WalletUtils . signMessage ( message , privKey ) ;
} ;
function _ initWcd ( network ) {
$ . checkArgument ( network ) ;
var xPrivKey = new Bitcore . HDPrivateKey ( network ) ;
var xPubKey = ( new Bitcore . HDPublicKey ( xPrivKey ) ) . toString ( ) ;
var roPrivKey = xPrivKey . derive ( 'm/1/0' ) . privateKey ;
var rwPrivKey = xPrivKey . derive ( 'm/1/1' ) . privateKey ;
var copayerId = WalletUtils . xPubToCopayerId ( xPubKey ) ;
return {
copayerId : copayerId ,
xPrivKey : xPrivKey . toString ( ) ,
publicKeyRing : [ xPubKey ] ,
network : network ,
roPrivKey : roPrivKey . toWIF ( ) ,
rwPrivKey : rwPrivKey . toWIF ( ) ,
} ;
} ;
function _ addWalletToWcd ( wcd , walletPrivKey , m , n ) {
$ . checkArgument ( wcd ) ;
var sharedEncryptingKey = WalletUtils . privateKeyToAESKey ( walletPrivKey ) ;
wcd . walletPrivKey = walletPrivKey . toWIF ( ) ;
wcd . sharedEncryptingKey = sharedEncryptingKey ;
wcd . m = m ;
wcd . n = n ;
} ;
function API ( opts ) {
if ( ! opts . storage ) {
@ -83,73 +118,106 @@ function API(opts) {
}
} ;
API . prototype . _ tryToCompleteFromServer = function ( wcd , cb ) {
API . prototype . _ tryToComplete = function ( data , cb ) {
var self = this ;
if ( ! wcd . walletPrivKey )
return cb ( 'Could not perform that action. Wallet Incomplete' ) ;
var self = this ;
var url = '/v1/wallets/' ;
self . _ doGetRequest ( url , data , function ( err , ret ) {
self . _ doGetRequest ( url , wc d, function ( err , ret ) {
if ( err ) return cb ( err ) ;
var wallet = ret . wallet ;
if ( wallet . status != 'complete' )
return cb ( 'Wallet Incomplete' ) ;
if ( ! Verifier . checkCopayers ( wallet . copayers , data . walletPrivKey ,
data . xPrivKey , data . n ) ) {
if ( ! Verifier . checkCopayers ( wallet . copayers , wc d. walletPrivKey ,
wc d. xPrivKey , wc d. n ) ) {
return cb ( new ServerCompromisedError (
'Copayers in the wallet could not be verified to have known the wallet secret' ) ) ;
}
data . publicKeyRing = _ . pluck ( wallet . copayers , 'xPubKey' )
wc d. publicKeyRing = _ . pluck ( wallet . copayers , 'xPubKey' )
self . storage . save ( data , function ( err ) {
return cb ( err , data ) ;
self . storage . save ( wc d, function ( err ) {
return cb ( err , wc d) ;
} ) ;
} ) ;
} ;
API . prototype . _ tryToCompleteFromData = function ( wcd , toComplete , cb ) {
var inData = _ decryptMessage ( toComplete ,
WalletUtils . privateKeyToAESKey ( wcd . roPrivKey ) ) ;
if ( ! inData )
return cb ( 'Could not complete wallet' ) ;
try {
inData = JSON . parse ( inData ) ;
_ . extend ( wcd , _ . pick ( inData , WALLET_AIRGAPPED_TOCOMPLETE ) ) ;
} catch ( ex ) {
return cb ( ex ) ;
}
this . storage . save ( wcd , function ( err ) {
return cb ( err , wcd ) ;
} ) ;
} ;
API . prototype . _ tryToComplete = function ( opts , wcd , cb ) {
if ( opts . toComplete ) {
this . _ tryToCompleteFromData ( wcd , opts . toComplete , cb ) ;
} else {
this . _ tryToCompleteFromServer ( wcd , cb ) ;
}
} ;
API . prototype . _ load = function ( cb ) {
var self = this ;
this . storage . load ( function ( err , data ) {
if ( err || ! data ) {
return cb ( err || 'Wallet file not found.' ) ;
this . storage . load ( function ( err , wc d) {
if ( err || ! wc d) {
return cb ( err || 'wcd file not found.' ) ;
}
return cb ( null , data ) ;
return cb ( null , wc d) ;
} ) ;
} ;
API . prototype . _ loadAndCheck = function ( cb ) {
/ * *
* _ loadAndCheck
*
* @ param opts . pkr
* /
API . prototype . _ loadAndCheck = function ( opts , cb ) {
var self = this ;
this . _ load ( function ( err , data ) {
this . _ load ( function ( err , wc d) {
if ( err ) return cb ( err ) ;
if ( data . n > 1 ) {
var pkrComplete = data . publicKeyRing && data . m && data . publicKeyRing . length === data . n ;
if ( ! pkrComplete ) {
return self . _ tryToComplete ( data , cb ) ;
}
if ( ! wcd . n || ( wcd . n > 1 && wcd . publicKeyRing . length != wcd . n ) ) {
return self . _ tryToComplete ( opts , wcd , cb ) ;
}
return cb ( null , data ) ;
return cb ( null , wcd ) ;
} ) ;
} ;
API . prototype . _ doRequest = function ( method , url , args , data , cb ) {
API . prototype . _ doRequest = function ( method , url , args , wc d, cb ) {
var reqSignature ;
data = data || { } ;
wc d = wc d || { } ;
if ( method == 'get' ) {
if ( data . roPrivKey )
reqSignature = _ signRequest ( method , url , args , data . roPrivKey ) ;
if ( wc d. roPrivKey )
reqSignature = _ signRequest ( method , url , args , wc d. roPrivKey ) ;
} else {
if ( data . rwPrivKey )
reqSignature = _ signRequest ( method , url , args , data . rwPrivKey ) ;
if ( wc d. rwPrivKey )
reqSignature = _ signRequest ( method , url , args , wc d. rwPrivKey ) ;
}
var absUrl = this . baseUrl + url ;
@ -157,7 +225,7 @@ API.prototype._doRequest = function(method, url, args, data, cb) {
// relUrl: only for testing with `supertest`
relUrl : this . basePath + url ,
headers : {
'x-identity' : data . copayerId ,
'x-identity' : wc d. copayerId ,
'x-signature' : reqSignature ,
} ,
method : method ,
@ -183,38 +251,15 @@ API.prototype._doRequest = function(method, url, args, data, cb) {
} ;
API . prototype . _ doPostRequest = function ( url , args , data , cb ) {
return this . _ doRequest ( 'post' , url , args , data , cb ) ;
API . prototype . _ doPostRequest = function ( url , args , wc d, cb ) {
return this . _ doRequest ( 'post' , url , args , wc d, cb ) ;
} ;
API . prototype . _ doGetRequest = function ( url , data , cb ) {
return this . _ doRequest ( 'get' , url , { } , data , cb ) ;
API . prototype . _ doGetRequest = function ( url , wc d, cb ) {
return this . _ doRequest ( 'get' , url , { } , wc d, cb ) ;
} ;
API . prototype . _ initData = function ( network , walletPrivKey , m , n ) {
var xPrivKey = new Bitcore . HDPrivateKey ( network ) ;
var xPubKey = ( new Bitcore . HDPublicKey ( xPrivKey ) ) . toString ( ) ;
var roPrivKey = xPrivKey . derive ( 'm/1/0' ) . privateKey ;
var rwPrivKey = xPrivKey . derive ( 'm/1/1' ) . privateKey ;
var sharedEncryptingKey = Bitcore . crypto . Hash . sha256 ( walletPrivKey . toBuffer ( ) ) . slice ( 0 , 16 ) . toString ( 'base64' ) ;
var copayerId = WalletUtils . xPubToCopayerId ( xPubKey ) ;
var data = {
copayerId : copayerId ,
xPrivKey : xPrivKey . toString ( ) ,
publicKeyRing : [ xPubKey ] ,
network : network ,
m : m ,
n : n ,
roPrivKey : roPrivKey . toWIF ( ) ,
rwPrivKey : rwPrivKey . toWIF ( ) ,
walletPrivKey : walletPrivKey . toWIF ( ) ,
sharedEncryptingKey : sharedEncryptingKey ,
} ;
return data ;
} ;
API . prototype . _ doJoinWallet = function ( walletId , walletPrivKey , xPubKey , copayerName , cb ) {
var args = {
walletId : walletId ,
@ -229,6 +274,22 @@ API.prototype._doJoinWallet = function(walletId, walletPrivKey, xPubKey, copayer
} ) ;
} ;
API . prototype . generateKey = function ( network , cb ) {
var self = this ;
network = network || 'livenet' ;
if ( ! _ . contains ( [ 'testnet' , 'livenet' ] , network ) )
return cb ( 'Invalid network' ) ;
this . storage . load ( function ( err , wcd ) {
if ( wcd )
return cb ( self . storage . getName ( ) + ' already contains a wallet' ) ;
var wcd = _ initWcd ( network ) ;
self . storage . save ( wcd , function ( err ) {
return cb ( err , null ) ;
} ) ;
} ) ;
} ;
API . prototype . createWallet = function ( walletName , copayerName , m , n , network , cb ) {
var self = this ;
@ -236,10 +297,13 @@ API.prototype.createWallet = function(walletName, copayerName, m, n, network, cb
if ( ! _ . contains ( [ 'testnet' , 'livenet' ] , network ) )
return cb ( 'Invalid network' ) ;
this . storage . load ( function ( err , data ) {
if ( data )
this . storage . load ( function ( err , wc d) {
if ( wcd && wcd . n )
return cb ( self . storage . getName ( ) + ' already contains a wallet' ) ;
if ( wcd && wcd . network && wcd . network != network )
return cb ( 'Storage ' + self . storage . getName ( ) + ' is set to network:' + wcd . network ) ;
var walletPrivKey = new Bitcore . PrivateKey ( ) ;
var args = {
name : walletName ,
@ -255,11 +319,14 @@ API.prototype.createWallet = function(walletName, copayerName, m, n, network, cb
var walletId = body . walletId ;
var secret = WalletUtils . toSecret ( walletId , walletPrivKey , network ) ;
var data = self . _ initData ( network , walletPrivKey , m , n ) ;
self . _ doJoinWallet ( walletId , walletPrivKey , data . publicKeyRing [ 0 ] , copayerName ,
wcd = wcd || _ initWcd ( network ) ;
_ addWalletToWcd ( wcd , walletPrivKey , m , n )
self . _ doJoinWallet ( walletId , walletPrivKey , wcd . publicKeyRing [ 0 ] , copayerName ,
function ( err , wallet ) {
if ( err ) return cb ( err ) ;
self . storage . save ( data , function ( err ) {
self . storage . save ( wc d, function ( err ) {
return cb ( err , n > 1 ? secret : null ) ;
} ) ;
} ) ;
@ -270,16 +337,16 @@ API.prototype.createWallet = function(walletName, copayerName, m, n, network, cb
API . prototype . reCreateWallet = function ( walletName , cb ) {
var self = this ;
this . _ loadAndCheck ( function ( err , data ) {
this . _ loadAndCheck ( { } , function ( err , wc d) {
if ( err ) return cb ( err ) ;
var walletPrivKey = new Bitcore . PrivateKey ( ) ;
var args = {
name : walletName ,
m : data . m ,
n : data . n ,
m : wc d. m ,
n : wc d. n ,
pubKey : walletPrivKey . toPublicKey ( ) . toString ( ) ,
network : data . network ,
network : wc d. network ,
} ;
var url = '/v1/wallets/' ;
self . _ doPostRequest ( url , args , { } , function ( err , body ) {
@ -287,11 +354,11 @@ API.prototype.reCreateWallet = function(walletName, cb) {
var walletId = body . walletId ;
var secret = WalletUtils . toSecret ( walletId , walletPrivKey , data . network ) ;
var secret = WalletUtils . toSecret ( walletId , walletPrivKey , wc d. network ) ;
var i = 0 ;
async . each ( data . publicKeyRing , function ( xpub , next ) {
async . each ( wc d. publicKeyRing , function ( xpub , next ) {
var copayerName = 'recovered Copayer #' + i ;
self . _ doJoinWallet ( walletId , walletPrivKey , data . publicKeyRing [ i ++ ] , copayerName , next ) ;
self . _ doJoinWallet ( walletId , walletPrivKey , wc d. publicKeyRing [ i ++ ] , copayerName , next ) ;
} , function ( err ) {
return cb ( err ) ;
} ) ;
@ -303,22 +370,22 @@ API.prototype.reCreateWallet = function(walletName, cb) {
API . prototype . joinWallet = function ( secret , copayerName , cb ) {
var self = this ;
this . storage . load ( function ( err , data ) {
if ( data )
return cb ( 'Storage already contains a wallet') ;
this . storage . load ( function ( err , wc d) {
if ( wcd && wcd . n )
return cb ( self . storage . getName ( ) + ' already contains a wallet') ;
try {
var secretData = WalletUtils . fromSecret ( secret ) ;
} catch ( ex ) {
return cb ( ex ) ;
}
var data = self . _ initData ( secretData . network , secretData . walletPrivKey ) ;
self . _ doJoinWallet ( secretData . walletId , secretData . walletPrivKey , data . publicKeyRing [ 0 ] , copayerName ,
function ( err , wallet ) {
wcd = wcd || _ initWcd ( secretData . network ) ;
self . _ doJoinWallet ( secretData . walletId , secretData . walletPrivKey , wcd . publicKeyRing [ 0 ] , copayerName ,
function ( err , joinedWallet ) {
if ( err ) return cb ( err ) ;
data . m = wallet . m ;
data . n = wallet . n ;
self . storage . save ( data , cb ) ;
_ addWalletToWcd ( wcd , secretData . walletPrivKey , joinedWallet . m , joinedWallet . n ) ;
self . storage . save ( wcd , cb ) ;
} ) ;
} ) ;
} ;
@ -326,13 +393,13 @@ API.prototype.joinWallet = function(secret, copayerName, cb) {
API . prototype . getStatus = function ( cb ) {
var self = this ;
this . _ load ( function ( err , data ) {
this . _ load ( function ( err , wc d) {
if ( err ) return cb ( err ) ;
var url = '/v1/wallets/' ;
self . _ doGetRequest ( url , data , function ( err , result ) {
_ processTxps ( result . pendingTxps , data . sharedEncryptingKey ) ;
return cb ( err , result , data . copayerId ) ;
self . _ doGetRequest ( url , wc d, function ( err , result ) {
_ processTxps ( result . pendingTxps , wc d. sharedEncryptingKey ) ;
return cb ( err , result , wc d. copayerId ) ;
} ) ;
} ) ;
} ;
@ -351,36 +418,36 @@ API.prototype.sendTxProposal = function(opts, cb) {
var self = this ;
this . _ loadAndCheck ( function ( err , data ) {
this . _ loadAndCheck ( { } , function ( err , wc d) {
if ( err ) return cb ( err ) ;
if ( ! data . rwPrivKey )
if ( ! wc d. rwPrivKey )
return cb ( 'No key to generate proposals' ) ;
var args = {
toAddress : opts . toAddress ,
amount : opts . amount ,
message : _ encryptMessage ( opts . message , data . sharedEncryptingKey ) ,
message : _ encryptMessage ( opts . message , wc d. sharedEncryptingKey ) ,
} ;
var hash = WalletUtils . getProposalHash ( args . toAddress , args . amount , args . message ) ;
args . proposalSignature = WalletUtils . signMessage ( hash , data . rwPrivKey ) ;
args . proposalSignature = WalletUtils . signMessage ( hash , wc d. rwPrivKey ) ;
log . debug ( 'Generating & signing tx proposal hash -> Hash: ' , hash , ' Signature: ' , args . proposalSignature ) ;
var url = '/v1/txproposals/' ;
self . _ doPostRequest ( url , args , data , cb ) ;
self . _ doPostRequest ( url , args , wc d, cb ) ;
} ) ;
} ;
API . prototype . createAddress = function ( cb ) {
var self = this ;
this . _ loadAndCheck ( function ( err , data ) {
this . _ loadAndCheck ( { } , function ( err , wc d) {
if ( err ) return cb ( err ) ;
var url = '/v1/addresses/' ;
self . _ doPostRequest ( url , { } , data , function ( err , address ) {
self . _ doPostRequest ( url , { } , wc d, function ( err , address ) {
if ( err ) return cb ( err ) ;
if ( ! Verifier . checkAddress ( data , address ) ) {
if ( ! Verifier . checkAddress ( wc d, address ) ) {
return cb ( new ServerCompromisedError ( 'Server sent fake address' ) ) ;
}
@ -396,16 +463,16 @@ API.prototype.createAddress = function(cb) {
API . prototype . getMainAddresses = function ( opts , cb ) {
var self = this ;
this . _ loadAndCheck ( function ( err , data ) {
this . _ loadAndCheck ( { } , function ( err , wc d) {
if ( err ) return cb ( err ) ;
var url = '/v1/addresses/' ;
self . _ doGetRequest ( url , data , function ( err , addresses ) {
self . _ doGetRequest ( url , wc d, function ( err , addresses ) {
if ( err ) return cb ( err ) ;
if ( ! opts . doNotVerify ) {
var fake = _ . any ( addresses , function ( address ) {
return ! Verifier . checkAddress ( data , address ) ;
return ! Verifier . checkAddress ( wc d, address ) ;
} ) ;
if ( fake )
return cb ( new ServerCompromisedError ( 'Server sent fake address' ) ) ;
@ -422,15 +489,16 @@ API.prototype.history = function(limit, cb) {
API . prototype . getBalance = function ( cb ) {
var self = this ;
this . _ loadAndCheck ( function ( err , data ) {
this . _ loadAndCheck ( { } , function ( err , wc d) {
if ( err ) return cb ( err ) ;
var url = '/v1/balance/' ;
self . _ doGetRequest ( url , data , cb ) ;
self . _ doGetRequest ( url , wc d, cb ) ;
} ) ;
} ;
/ * *
* export
* Export does not try to complete the wallet from the server . Exports the
* wallet as it is now .
*
* @ param opts . access = [ 'full' , 'readonly' , 'readwrite' ]
* /
@ -440,11 +508,11 @@ API.prototype.export = function(opts, cb) {
opts = opts || { } ;
var access = opts . access || 'full' ;
this . _ loadAndCheck ( function ( err , data ) {
this . _ load ( function ( err , wc d) {
if ( err ) return cb ( err ) ;
var v = [ ] ;
var myXPubKey = ( new Bitcore . HDPublicKey ( data . xPrivKey ) ) . toString ( ) ;
var myXPubKey = ( new Bitcore . HDPublicKey ( wc d. xPrivKey ) ) . toString ( ) ;
_ . each ( WALLET_CRITICAL_DATA , function ( k ) {
var d ;
@ -456,18 +524,18 @@ API.prototype.export = function(opts, cb) {
// Skips own pub key IF priv key is exported
if ( access == 'full' && k === 'publicKeyRing' ) {
d = _ . without ( data [ k ] , myXPubKey ) ;
d = _ . without ( wc d[ k ] , myXPubKey ) ;
} else {
d = data [ k ] ;
d = wc d[ k ] ;
}
v . push ( d ) ;
} ) ;
if ( access != 'full' ) {
v . push ( data . copayerId ) ;
v . push ( data . roPrivKey ) ;
v . push ( wc d. copayerId ) ;
v . push ( wc d. roPrivKey ) ;
if ( access == 'readwrite' ) {
v . push ( data . rwPrivKey ) ;
v . push ( wc d. rwPrivKey ) ;
}
}
@ -479,36 +547,34 @@ API.prototype.export = function(opts, cb) {
API . prototype . import = function ( str , cb ) {
var self = this ;
this . storage . load ( function ( err , data ) {
if ( data )
this . storage . load ( function ( err , wc d) {
if ( wc d)
return cb ( 'Storage already contains a wallet' ) ;
data = { } ;
wc d = { } ;
var inData = JSON . parse ( str ) ;
var i = 0 ;
_ . each ( WALLET_CRITICAL_DATA . concat ( WALLET_EXTRA_DATA ) , function ( k ) {
data [ k ] = inData [ i ++ ] ;
wc d[ k ] = inData [ i ++ ] ;
} ) ;
if ( data . xPrivKey ) {
var xpriv = new Bitcore . HDPrivateKey ( data . xPrivKey ) ;
if ( wc d. xPrivKey ) {
var xpriv = new Bitcore . HDPrivateKey ( wc d. xPrivKey ) ;
var xPubKey = new Bitcore . HDPublicKey ( xpriv ) . toString ( ) ;
data . publicKeyRing . unshift ( xPubKey ) ;
data . copayerId = WalletUtils . xPubToCopayerId ( xPubKey ) ;
data . roPrivKey = xpriv . derive ( 'm/1/0' ) . privateKey . toWIF ( ) ;
data . rwPrivKey = xpriv . derive ( 'm/1/1' ) . privateKey . toWIF ( ) ;
wc d. publicKeyRing . unshift ( xPubKey ) ;
wc d. copayerId = WalletUtils . xPubToCopayerId ( xPubKey ) ;
wc d. roPrivKey = xpriv . derive ( 'm/1/0' ) . privateKey . toWIF ( ) ;
wc d. rwPrivKey = xpriv . derive ( 'm/1/1' ) . privateKey . toWIF ( ) ;
}
data . n = data . publicKeyRing . length ;
if ( ! data . copayerId || ! data . n || ! data . m )
return cb ( 'Invalid source data' ) ;
if ( ! wcd . publicKeyRing )
return cb ( 'Invalid source wallet' ) ;
data . network = data . publicKeyRing [ 0 ] . substr ( 0 , 4 ) == 'tpub' ? 'testnet' : 'livenet' ;
self . storage . save ( data , function ( err ) {
return cb ( err , WalletUtils . accessFromData ( data ) ) ;
wcd . network = wcd . publicKeyRing [ 0 ] . substr ( 0 , 4 ) == 'tpub' ? 'testnet' : 'livenet' ;
self . storage . save ( wc d, function ( err ) {
return cb ( err , WalletUtils . accessFromData ( wc d) ) ;
} ) ;
} ) ;
} ;
@ -517,23 +583,19 @@ API.prototype.import = function(str, cb) {
*
* /
API . prototype . parseTxProposals = function ( txps , cb ) {
API . prototype . parseTxProposals = function ( txData , cb ) {
var self = this ;
this . _ load ( function ( err , data ) {
this . _ loadAndCheck ( {
toComplete : txData . toComplete
} , function ( err , wcd ) {
if ( err ) return cb ( err ) ;
if ( data . n > 1 ) {
var pkrComplete = data . publicKeyRing && data . m && data . publicKeyRing . length === data . n ;
if ( ! pkrComplete ) {
return cb ( 'Wallet Incomplete' ) ;
}
}
_ processTxps ( txps , data . sharedEncryptingKey ) ;
var txps = txData . txps ;
_ processTxps ( txps , wcd . sharedEncryptingKey ) ;
var fake = _ . any ( txps , function ( txp ) {
return ( ! Verifier . checkTxProposal ( data , txp ) ) ;
return ( ! Verifier . checkTxProposal ( wcd , txp ) ) ;
} ) ;
if ( fake )
@ -555,20 +617,20 @@ API.prototype.parseTxProposals = function(txps, cb) {
API . prototype . getTxProposals = function ( opts , cb ) {
var self = this ;
this . _ loadAndCheck ( function ( err , data ) {
this . _ loadAndCheck ( { } , function ( err , wc d) {
if ( err ) return cb ( err ) ;
var url = '/v1/txproposals/' ;
self . _ doGetRequest ( url , data , function ( err , txps ) {
self . _ doGetRequest ( url , wc d, function ( err , txps ) {
if ( err ) return cb ( err ) ;
var rawTxps ;
if ( opts . getRawTxps )
rawTxps = JSON . parse ( JSON . stringify ( txps ) ) ;
_ processTxps ( txps , data . sharedEncryptingKey ) ;
_ processTxps ( txps , wc d. sharedEncryptingKey ) ;
var fake = _ . any ( txps , function ( txp ) {
return ( ! opts . doNotVerify && ! Verifier . checkTxProposal ( data , txp ) ) ;
return ( ! opts . doNotVerify && ! Verifier . checkTxProposal ( wc d, txp ) ) ;
} ) ;
if ( fake )
@ -579,14 +641,14 @@ API.prototype.getTxProposals = function(opts, cb) {
} ) ;
} ;
API . prototype . _ getSignaturesFor = function ( txp , data ) {
API . prototype . _ getSignaturesFor = function ( txp , wc d) {
//Derive proper key to sign, for each input
var privs = [ ] ,
derived = { } ;
var network = new Bitcore . Address ( txp . toAddress ) . network . name ;
var xpriv = new Bitcore . HDPrivateKey ( data . xPrivKey , network ) ;
var xpriv = new Bitcore . HDPrivateKey ( wc d. xPrivKey , network ) ;
_ . each ( txp . inputs , function ( i ) {
if ( ! derived [ i . path ] ) {
@ -619,37 +681,49 @@ API.prototype.getSignatures = function(txp, cb) {
$ . checkArgument ( txp . creatorId ) ;
var self = this ;
this . _ loadAndCheck ( function ( err , data ) {
this . _ loadAndCheck ( { } , function ( err , wc d) {
if ( err ) return cb ( err ) ;
if ( ! Verifier . checkTxProposal ( data , txp ) ) {
if ( ! Verifier . checkTxProposal ( wc d, txp ) ) {
return cb ( new ServerCompromisedError ( 'Transaction proposal is invalid' ) ) ;
}
return cb ( null , self . _ getSignaturesFor ( txp , data ) ) ;
return cb ( null , self . _ getSignaturesFor ( txp , wc d) ) ;
} ) ;
} ;
API . prototype . getEncryptedWalletData = function ( cb ) {
var self = this ;
this . _ loadAndCheck ( { } , function ( err , wcd ) {
if ( err ) return cb ( err ) ;
var toComplete = JSON . stringify ( _ . pick ( wcd , WALLET_AIRGAPPED_TOCOMPLETE ) ) ;
return cb ( null , _ encryptMessage ( toComplete , WalletUtils . privateKeyToAESKey ( wcd . roPrivKey ) ) ) ;
} ) ;
} ;
API . prototype . signTxProposal = function ( txp , cb ) {
$ . checkArgument ( txp . creatorId ) ;
var self = this ;
this . _ loadAndCheck ( function ( err , data ) {
this . _ loadAndCheck ( { } , function ( err , wc d) {
if ( err ) return cb ( err ) ;
if ( ! Verifier . checkTxProposal ( data , txp ) ) {
if ( ! Verifier . checkTxProposal ( wc d, txp ) ) {
return cb ( new ServerCompromisedError ( 'Server sent fake transaction proposal' ) ) ;
}
var signatures = txp . signatures || self . _ getSignaturesFor ( txp , data ) ;
var signatures = txp . signatures || self . _ getSignaturesFor ( txp , wc d) ;
var url = '/v1/txproposals/' + txp . id + '/signatures/' ;
var args = {
signatures : signatures
} ;
self . _ doPostRequest ( url , args , data , cb ) ;
self . _ doPostRequest ( url , args , wc d, cb ) ;
} ) ;
} ;
@ -658,27 +732,27 @@ API.prototype.rejectTxProposal = function(txp, reason, cb) {
var self = this ;
this . _ loadAndCheck (
function ( err , data ) {
this . _ loadAndCheck ( { } ,
function ( err , wc d) {
if ( err ) return cb ( err ) ;
var url = '/v1/txproposals/' + txp . id + '/rejections/' ;
var args = {
reason : _ encryptMessage ( reason , data . sharedEncryptingKey ) || '' ,
reason : _ encryptMessage ( reason , wc d. sharedEncryptingKey ) || '' ,
} ;
self . _ doPostRequest ( url , args , data , cb ) ;
self . _ doPostRequest ( url , args , wc d, cb ) ;
} ) ;
} ;
API . prototype . broadcastTxProposal = function ( txp , cb ) {
var self = this ;
this . _ loadAndCheck (
function ( err , data ) {
this . _ loadAndCheck ( { } ,
function ( err , wc d) {
if ( err ) return cb ( err ) ;
var url = '/v1/txproposals/' + txp . id + '/broadcast/' ;
self . _ doPostRequest ( url , { } , data , cb ) ;
self . _ doPostRequest ( url , { } , wc d, cb ) ;
} ) ;
} ;
@ -686,11 +760,11 @@ API.prototype.broadcastTxProposal = function(txp, cb) {
API . prototype . removeTxProposal = function ( txp , cb ) {
var self = this ;
this . _ loadAndCheck (
function ( err , data ) {
this . _ loadAndCheck ( { } ,
function ( err , wc d) {
if ( err ) return cb ( err ) ;
var url = '/v1/txproposals/' + txp . id ;
self . _ doRequest ( 'delete' , url , { } , data , cb ) ;
self . _ doRequest ( 'delete' , url , { } , wc d, cb ) ;
} ) ;
} ;