@ -62,28 +62,24 @@ function API(opts) {
} ;
API . prototype . _ loadAndCheck = function ( opts ) {
var data = this . storage . load ( ) ;
if ( ! data ) {
log . error ( 'Wallet file not found.' ) ;
process . exit ( 1 ) ;
}
if ( data . verified == 'corrupt' ) {
throw new Error ( 'The wallet is tagged as corrupt. Some of the copayers cannot be verified to have known the wallet secret.' ) ;
}
if ( data . n > 1 ) {
var pkrComplete = data . publicKeyRing && data . m && data . publicKeyRing . length === data . n ;
API . prototype . _ loadAndCheck = function ( opts , cb ) {
this . storage . load ( function ( err , data ) {
if ( err || ! data ) {
return cb ( err || 'Wallet file not found.' ) ;
}
if ( opts . requireCompletePKR && ! pkrComplete ) {
throw new Error ( 'Wallet Incomplete, cannot derive address .') ;
if ( data . verified == 'corrupt' ) {
return cb ( 'The wallet is tagged as corrupt. Some of the copayers cannot be verified to have known the wallet secret.' ) ;
}
if ( data . n > 1 ) {
var pkrComplete = data . publicKeyRing && data . m && data . publicKeyRing . length === data . n ;
if ( ! pkrComplete ) {
log . warn ( 'The file ' + this . filename + ' is incomplete. It will allow you to operate with the wallet but it should not be trusted as a backup. Please wait for all copayers to join the wallet and run the tool with -export flag.' )
if ( opts . requireCompletePKR && ! pkrComplete ) {
return cb ( 'Wallet Incomplete, cannot derive address' ) ;
}
}
}
return data ;
return cb ( null , data ) ;
} ) ;
} ;
API . prototype . _ doRequest = function ( method , url , args , data , cb ) {
@ -130,44 +126,48 @@ API.prototype.createWallet = function(walletName, copayerName, m, n, network, cb
if ( ! _ . contains ( [ 'testnet' , 'livenet' ] , network ) )
return cb ( 'Invalid network' ) ;
var data = this . storage . load ( ) ;
if ( data ) return cb ( 'File ' + this . filename + ' already contains a wallet' ) ;
// Generate wallet key pair to verify copayers
var privKey = new Bitcore . PrivateKey ( null , network ) ;
var pubKey = privKey . toPublicKey ( ) ;
data = {
m : m ,
n : n ,
walletPrivKey : privKey . toWIF ( ) ,
network : network ,
} ;
var args = {
name : walletName ,
m : m ,
n : n ,
pubKey : pubKey . toString ( ) ,
network : network ,
} ;
var url = '/v1/wallets/' ;
this . _ doPostRequest ( url , args , data , function ( err , body ) {
if ( err ) return cb ( err ) ;
this . storage . load ( function ( err , data ) {
if ( data )
return cb ( 'Storage already contains a wallet' ) ;
console . log ( '[API.js.132]' ) ; //TODO
// Generate wallet key pair to verify copayers
var privKey = new Bitcore . PrivateKey ( null , network ) ;
var pubKey = privKey . toPublicKey ( ) ;
data = {
m : m ,
n : n ,
walletPrivKey : privKey . toWIF ( ) ,
network : network ,
} ;
var args = {
name : walletName ,
m : m ,
n : n ,
pubKey : pubKey . toString ( ) ,
network : network ,
} ;
var url = '/v1/wallets/' ;
self . _ doPostRequest ( url , args , data , function ( err , body ) {
if ( err ) return cb ( err ) ;
var walletId = body . walletId ;
var secret = walletId + ':' + privKey . toString ( ) + ':' + ( network == 'testnet' ? 'T' : 'L' ) ;
var ret ;
var walletId = body . walletId ;
var secret = walletId + ':' + privKey . toString ( ) + ':' + ( network == 'testnet' ? 'T' : 'L' ) ;
var ret ;
if ( n > 1 )
ret = data . secret = secret ;
if ( n > 1 )
ret = data . secret = secret ;
self . storage . save ( data ) ;
self . _ joinWallet ( data , secret , copayerName , function ( err ) {
if ( err ) return cb ( err ) ;
self . storage . save ( data , function ( err ) {
if ( err ) return cb ( err ) ;
self . _ joinWallet ( data , secret , copayerName , function ( err ) {
return cb ( err , ret ) ;
} ) ;
return cb ( null , ret ) ;
} ) ;
} ) ;
} ) ;
} ;
@ -204,54 +204,58 @@ API.prototype._joinWallet = function(data, secret, copayerName, cb) {
data . n = wallet . n ;
data . publicKeyRing = wallet . publicKeyRing ;
data . network = wallet . network ,
self . storage . save ( data ) ;
return cb ( ) ;
self . storage . save ( data , cb ) ;
} ) ;
} ;
API . prototype . joinWallet = function ( secret , copayerName , cb ) {
var self = this ;
var data = this . storage . load ( ) ;
if ( data ) return cb ( 'File ' + this . filename + ' already contains a wallet' ) ;
this . storage . load ( function ( err , data ) {
if ( data )
return cb ( 'Storage already contains a wallet' ) ;
self . _ joinWallet ( data , secret , copayerName , cb ) ;
self . _ joinWallet ( data , secret , copayerName , cb ) ;
} ) ;
} ;
API . prototype . getStatus = function ( cb ) {
var self = this ;
var data = this . _ loadAndCheck ( ) ;
var url = '/v1/wallets/' ;
this . _ doGetRequest ( url , data , function ( err , body ) {
this . _ loadAndCheck ( { } , function ( err , data ) {
if ( err ) return cb ( err ) ;
var wallet = body ;
if ( wallet . n > 0 && wallet . status === 'complete' && ! data . verified ) {
var pubKey = Bitcore . PrivateKey . fromString ( data . walletPrivKey ) . toPublicKey ( ) . toString ( ) ;
var fake = [ ] ;
_ . each ( wallet . copayers , function ( copayer ) {
console . log ( '[clilib.js.224]' , copayer . xPubKey , copayer . xPubKeySignature , pubKey ) ; //TODO
if ( ! SignUtils . verify ( copayer . xPubKey , copayer . xPubKeySignature , pubKey ) ) {
var url = '/v1/wallets/' ;
self . _ doGetRequest ( url , data , function ( err , body ) {
if ( err ) return cb ( err ) ;
console . log ( '[clilib.js.227] FAKE' ) ; //TODO
fake . push ( copayer ) ;
var wallet = body ;
if ( wallet . n > 0 && wallet . status === 'complete' && ! data . verified ) {
var pubKey = Bitcore . PrivateKey . fromString ( data . walletPrivKey ) . toPublicKey ( ) . toString ( ) ;
var fake = [ ] ;
_ . each ( wallet . copayers , function ( copayer ) {
console . log ( '[clilib.js.224]' , copayer . xPubKey , copayer . xPubKeySignature , pubKey ) ; //TODO
if ( ! SignUtils . verify ( copayer . xPubKey , copayer . xPubKeySignature , pubKey ) ) {
console . log ( '[clilib.js.227] FAKE' ) ; //TODO
fake . push ( copayer ) ;
}
} ) ;
if ( fake . length > 0 ) {
log . error ( 'Some copayers in the wallet could not be verified to have known the wallet secret' ) ;
data . verified = 'corrupt' ;
} else {
data . verified = 'ok' ;
}
} ) ;
if ( fake . length > 0 ) {
log . error ( 'Some copayers in the wallet could not be verified to have known the wallet secret' ) ;
data . verified = 'corrupt' ;
} else {
data . verified = 'ok' ;
self . storage . save ( data , function ( err ) {
return cb ( err , wallet ) ;
} ) ;
}
self . storage . save ( data ) ;
}
return cb ( null , wallet ) ;
return cb ( null , wallet ) ;
} ) ;
} ) ;
} ;
@ -266,38 +270,35 @@ API.prototype.getStatus = function(cb) {
API . prototype . sendTxProposal = function ( inArgs , cb ) {
var self = this ;
var data = this . _ loadAndCheck ( ) ;
var args = _ createProposalOpts ( inArgs , data . signingPrivKey ) ;
var url = '/v1/txproposals/' ;
this . _ doPostRequest ( url , args , data , cb ) ;
} ;
// Get addresses
API . prototype . getAddresses = function ( cb ) {
var self = this ;
this . _ loadAndCheck ( {
requireCompletePKR : true
} , function ( err , data ) {
if ( err ) return cb ( err ) ;
var data = this . _ loadAndCheck ( ) ;
var args = _ createProposalOpts ( inArgs , data . signingPrivKey ) ;
var url = '/v1/addresses/' ;
this . _ doGetRequest ( url , data , cb ) ;
var url = '/v1/txproposals/' ;
self . _ doPostRequest ( url , args , data , cb ) ;
} ) ;
} ;
// Creates a new address
// TODO: verify derivation!!
API . prototype . createAddress = function ( cb ) {
var self = this ;
var data = this . _ loadAndCheck ( { requireCompletePKR : true } ) ;
var url = '/v1/addresses/' ;
this . _ doPostRequest ( url , { } , data , function ( err , address ) {
this . _ loadAndCheck ( {
requireCompletePKR : true
} , function ( err , dat a) {
if ( err ) return cb ( err ) ;
if ( ! Verifier . checkAddress ( data , address ) ) {
return cb ( new ServerCompromisedError ( 'Server sent fake address' ) ) ;
}
return cb ( null , address ) ;
var url = '/v1/addresses/' ;
self . _ doPostRequest ( url , { } , data , function ( err , address ) {
if ( err ) return cb ( err ) ;
if ( ! Verifier . checkAddress ( data , address ) ) {
return cb ( new ServerCompromisedError ( 'Server sent fake address' ) ) ;
}
return cb ( null , address ) ;
} ) ;
} ) ;
} ;
@ -308,92 +309,113 @@ API.prototype.history = function(limit, cb) {
API . prototype . getBalance = function ( cb ) {
var self = this ;
var data = this . _ loadAndCheck ( ) ;
var url = '/v1/balance/' ;
this . _ doGetRequest ( url , data , cb ) ;
this . _ loadAndCheck ( { } , function ( err , data ) {
if ( err ) return cb ( err ) ;
var url = '/v1/balance/' ;
self . _ doGetRequest ( url , data , cb ) ;
} ) ;
} ;
API . prototype . getTxProposals = function ( opts , cb ) {
var self = this ;
var data = this . _ loadAndCheck ( ) ;
var url = '/v1/txproposals/' ;
this . _ doGetRequest ( url , data , cb ) ;
this . _ loadAndCheck ( {
requireCompletePKR : true
} , function ( err , data ) {
if ( err ) return cb ( err ) ;
var url = '/v1/txproposals/' ;
self . _ doGetRequest ( url , data , cb ) ;
} ) ;
} ;
API . prototype . signTxProposal = function ( txp , cb ) {
var self = this ;
var data = this . _ loadAndCheck ( ) ;
this . _ loadAndCheck ( {
requireCompletePKR : true
} , function ( err , data ) {
if ( err ) return cb ( err ) ;
//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 ) ;
//Derive proper key to sign, for each input
var privs = [ ] ,
derived = { } ;
_ . each ( txp . inputs , function ( i ) {
if ( ! derived [ i . path ] ) {
derived [ i . path ] = xpriv . derive ( i . path ) . privateKey ;
}
privs . push ( derived [ i . path ] ) ;
} ) ;
var network = new Bitcore . Address ( txp . toAddress ) . network . name ;
var xpriv = new Bitcore . HDPrivateKey ( data . xPrivKey , network ) ;
var t = new Bitcore . Transaction ( ) ;
_ . each ( txp . inputs , function ( i ) {
t . from ( i , i . publicKeys , txp . requiredSignatures ) ;
} ) ;
_ . each ( txp . inputs , function ( i ) {
if ( ! derived [ i . path ] ) {
derived [ i . path ] = xpriv . derive ( i . path ) . privateKey ;
}
privs . push ( derived [ i . path ] ) ;
} ) ;
t . to ( txp . toAddress , txp . amount )
. change ( txp . changeAddress )
. sign ( privs ) ;
var t = new Bitcore . Transaction ( ) ;
_ . each ( txp . inputs , function ( i ) {
t . from ( i , i . publicKeys , txp . requiredSignatures ) ;
} ) ;
var signatures = [ ] ;
_ . each ( privs , function ( p ) {
var s = t . getSignatures ( p ) [ 0 ] . signature . toDER ( ) . toString ( 'hex' ) ;
signatures . push ( s ) ;
} ) ;
t . to ( txp . toAddress , txp . amount )
. change ( txp . changeAddress )
. sign ( privs ) ;
var url = '/v1/txproposals/' + txp . id + '/signatures/' ;
var args = {
signatures : signatures
} ;
var signatures = [ ] ;
_ . each ( privs , function ( p ) {
var s = t . getSignatures ( p ) [ 0 ] . signature . toDER ( ) . toString ( 'hex' ) ;
signatures . push ( s ) ;
} ) ;
this . _ doPostRequest ( url , args , data , cb ) ;
var url = '/v1/txproposals/' + txp . id + '/signatures/' ;
var args = {
signatures : signatures
} ;
self . _ doPostRequest ( url , args , data , cb ) ;
} ) ;
} ;
API . prototype . rejectTxProposal = function ( txp , reason , cb ) {
var self = this ;
var data = this . _ loadAndCheck ( ) ;
var url = '/v1/txproposals/' + txp . id + '/rejections/' ;
var args = {
reason : reason || '' ,
} ;
this . _ doPostRequest ( url , args , data , cb ) ;
this . _ loadAndCheck ( {
requireCompletePKR : true
} , function ( err , data ) {
if ( err ) return cb ( err ) ;
var url = '/v1/txproposals/' + txp . id + '/rejections/' ;
var args = {
reason : reason || '' ,
} ;
self . _ doPostRequest ( url , args , data , cb ) ;
} ) ;
} ;
API . prototype . broadcastTxProposal = function ( txp , cb ) {
var self = this ;
var data = this . _ loadAndCheck ( ) ;
var url = '/v1/txproposals/' + txp . id + '/broadcast/' ;
this . _ doPostRequest ( url , { } , data , cb ) ;
this . _ loadAndCheck ( {
requireCompletePKR : true
} , function ( err , data ) {
if ( err ) return cb ( err ) ;
var url = '/v1/txproposals/' + txp . id + '/broadcast/' ;
self . _ doPostRequest ( url , { } , data , cb ) ;
} ) ;
} ;
API . prototype . removeTxProposal = function ( txp , cb ) {
var self = this ;
var data = this . _ loadAndCheck ( ) ;
var url = '/v1/txproposals/' + txp . id ;
this . _ doRequest ( 'delete' , url , { } , data , cb ) ;
this . _ loadAndCheck ( {
requireCompletePKR : true
} , function ( err , data ) {
if ( err ) return cb ( err ) ;
var url = '/v1/txproposals/' + txp . id ;
self . _ doRequest ( 'delete' , url , { } , data , cb ) ;
} ) ;
} ;
module . exports = API ;