@ -16,6 +16,7 @@ var ServerCompromisedError = require('./servercompromisederror')
var BASE_URL = 'http://localhost:3001/copay/api' ;
var WALLET_CRITICAL_DATA = [ 'xPrivKey' , 'm' , 'publicKeyRing' , 'sharedEncryptingKey' ] ;
var WALLET_EXTRA_DATA = [ 'copayerId' , 'roPrivKey' , 'rwPrivKey' ] ;
function _ encryptMessage ( message , encryptingKey ) {
if ( ! message ) return null ;
@ -94,9 +95,12 @@ API.prototype._tryToComplete = function(data, cb) {
if ( wallet . status != 'complete' )
return cb ( 'Wallet Incomplete' ) ;
if ( ! Verifier . checkCopayers ( wallet . copayers , data . walletPrivKey , data . xPrivKey , data . n ) )
if ( ! Verifier . checkCopayers ( wallet . copayers , data . walletPrivKey ,
data . xPrivKey , data . 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' )
@ -140,8 +144,13 @@ API.prototype._doRequest = function(method, url, args, data, cb) {
var reqSignature ;
data = data || { } ;
if ( data . signingPrivKey )
reqSignature = _ signRequest ( method , url , args , data . signingPrivKey ) ;
if ( method == 'get' ) {
if ( data . roPrivKey )
reqSignature = _ signRequest ( method , url , args , data . roPrivKey ) ;
} else {
if ( data . rwPrivKey )
reqSignature = _ signRequest ( method , url , args , data . rwPrivKey ) ;
}
var absUrl = this . baseUrl + url ;
var args = {
@ -186,7 +195,8 @@ API.prototype._doGetRequest = function(url, data, cb) {
API . prototype . _ initData = function ( network , walletPrivKey , m , n ) {
var xPrivKey = new Bitcore . HDPrivateKey ( network ) ;
var xPubKey = ( new Bitcore . HDPublicKey ( xPrivKey ) ) . toString ( ) ;
var signingPrivKey = ( new Bitcore . HDPrivateKey ( xPrivKey ) ) . derive ( 'm/1/0' ) . privateKey ;
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 ) ;
@ -197,7 +207,8 @@ API.prototype._initData = function(network, walletPrivKey, m, n) {
network : network ,
m : m ,
n : n ,
signingPrivKey : signingPrivKey . toWIF ( ) ,
roPrivKey : roPrivKey . toWIF ( ) ,
rwPrivKey : rwPrivKey . toWIF ( ) ,
walletPrivKey : walletPrivKey . toWIF ( ) ,
sharedEncryptingKey : sharedEncryptingKey ,
} ;
@ -339,13 +350,16 @@ API.prototype.sendTxProposal = function(opts, cb) {
this . _ loadAndCheck ( function ( err , data ) {
if ( err ) return cb ( err ) ;
if ( ! data . rwPrivKey )
return cb ( 'No key to generate proposals' ) ;
var args = {
toAddress : opts . toAddress ,
amount : opts . amount ,
message : _ encryptMessage ( opts . message , data . sharedEncryptingKey ) ,
} ;
var hash = WalletUtils . getProposalHash ( args . toAddress , args . amount , args . message ) ;
args . proposalSignature = WalletUtils . signMessage ( hash , data . signing PrivKey) ;
args . proposalSignature = WalletUtils . signMessage ( hash , data . rw PrivKey) ;
log . debug ( 'Generating & signing tx proposal hash -> Hash: ' , hash , ' Signature: ' , args . proposalSignature ) ;
var url = '/v1/txproposals/' ;
@ -385,8 +399,16 @@ API.prototype.getBalance = function(cb) {
} ) ;
} ;
API . prototype . export = function ( cb ) {
/ * *
* export
*
* @ param opts . access = [ 'full' , 'readonly' , 'readwrite' ]
* /
API . prototype . export = function ( opts , cb ) {
var self = this ;
$ . shouldBeFunction ( cb ) ;
opts = opts || { } ;
var access = opts . access || 'full' ;
this . _ loadAndCheck ( function ( err , data ) {
if ( err ) return cb ( err ) ;
@ -396,13 +418,29 @@ API.prototype.export = function(cb) {
_ . each ( WALLET_CRITICAL_DATA , function ( k ) {
var d ;
if ( k === 'publicKeyRing' ) {
if ( access != 'full' && k === 'xPrivKey' ) {
v . push ( null ) ;
return ;
}
// Skips own pub key IF priv key is exported
if ( access == 'full' && k === 'publicKeyRing' ) {
d = _ . without ( data [ k ] , myXPubKey ) ;
} else {
d = data [ k ] ;
}
v . push ( d ) ;
} ) ;
if ( access != 'full' ) {
v . push ( data . copayerId ) ;
v . push ( data . roPrivKey ) ;
if ( access == 'readwrite' ) {
v . push ( data . rwPrivKey ) ;
}
}
return cb ( null , JSON . stringify ( v ) ) ;
} ) ;
}
@ -420,20 +458,28 @@ API.prototype.import = function(str, cb) {
var inData = JSON . parse ( str ) ;
var i = 0 ;
_ . each ( WALLET_CRITICAL_DATA , function ( k ) {
_ . each ( WALLET_CRITICAL_DATA . concat ( WALLET_EXTRA_DATA ) , function ( k ) {
data [ k ] = inData [ i ++ ] ;
if ( ! data [ k ] )
return cb ( 'Invalid wallet data' ) ;
} ) ;
var xPubKey = ( new Bitcore . HDPublicKey ( data . xPrivKey ) ) . toString ( ) ;
if ( data . xPrivKey ) {
var xpriv = new Bitcore . HDPrivateKey ( data . 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 ( ) ;
}
data . n = data . publicKeyRing . length ;
data . signingPrivKey = ( new Bitcore . HDPrivateKey ( data . xPrivKey ) ) . derive ( 'm/1/0' ) . privateKey . toWIF ( ) ;
data . network = data . xPrivKey . substr ( 0 , 4 ) === 'tprv' ? 'testnet' : 'livenet' ;
self . storage . save ( data , cb ) ;
if ( ! data . copayerId || ! data . n || ! data . m )
return cb ( 'Invalid source data' ) ;
data . network = data . publicKeyRing [ 0 ] . substr ( 0 , 4 ) == 'tpub' ? 'testnet' : 'livenet' ;
self . storage . save ( data , function ( err ) {
return cb ( err , WalletUtils . accessFromData ( data ) ) ;
} ) ;
} ) ;
} ;