2014-10-30 06:38:14 -07:00
#!/usr/bin/env node
2014-11-06 09:53:09 -08:00
2014-10-30 06:38:14 -07:00
'use strict' ;
2014-11-02 15:32:12 -08:00
2014-10-30 06:38:14 -07:00
var copay = require ( '../copay' ) ;
var _ = require ( 'lodash' ) ;
var config = require ( '../config' ) ;
var version = require ( '../version' ) . version ;
var sinon = require ( 'sinon' ) ;
var bitcore = require ( 'bitcore' ) ;
var readline = require ( 'readline' ) ;
2014-11-01 05:42:56 -07:00
var async = require ( 'async' ) ;
2014-11-02 18:53:47 -08:00
var program = require ( 'commander' ) ;
2014-10-30 06:38:14 -07:00
2014-11-02 18:53:47 -08:00
function list ( val ) {
return val . split ( ',' ) ;
}
2014-11-02 15:32:12 -08:00
2014-11-02 18:53:47 -08:00
program
. version ( '0.0.1' )
. usage ( '-d n2kMqQ8Si9GndzQ6FrJxcwHMKacK2rCEpK -n 2 -k tprv8ZgxMBicQKsPem5BuuDT6xY9etUC2RohpUoyzoa1MEkkZyAHhszaHPZTmgDheN31hSP1r6bRwpj2JC66r1CPpftwaRrhz' )
. option ( '-d, --destination <n>' , 'Destination Address' )
. option ( '-n, --required <n>' , 'Required number of signatures' , parseInt )
2014-11-03 10:47:35 -08:00
. option ( '-k, --keys <items>' , 'master private keys, separated by , ' , list )
2014-11-03 04:55:44 -08:00
. option ( '-a, --amount <n>' , 'Optional, amount to transfer, in Satoshis. If not provided, will wipe all funds' , parseInt )
. option ( '-f, --fee [n]' , 'Optional, fee in BTC (default 0.0001 BTC), only if amount is not provided' , parseFloat )
2014-11-02 18:53:47 -08:00
. parse ( process . argv ) ;
2014-11-02 15:32:12 -08:00
2014-10-30 06:38:14 -07:00
var rl = readline . createInterface ( {
input : process . stdin ,
output : process . stdout
} ) ;
var args = process . argv ;
2014-11-02 18:53:47 -08:00
var requiredCopayers = program . required ;
var extPrivKeys = program . keys ;
var destAddr = program . destination ;
2014-11-03 03:40:53 -08:00
var amount = program . amount ;
2014-10-30 06:38:14 -07:00
2014-11-03 04:55:44 -08:00
if ( amount && program . fee ) {
console . log ( 'If amount if given, fee will be automatically calculated' ) ;
process . exit ( 1 ) ;
}
2014-11-06 09:53:09 -08:00
if ( ! requiredCopayers || ! extPrivKeys || ! extPrivKeys . length || ! destAddr ) {
2014-11-02 18:53:47 -08:00
program . outputHelp ( ) ;
2014-10-30 07:49:05 -07:00
process . exit ( 1 ) ;
}
2014-11-03 04:55:44 -08:00
// Fee to asign to the tx. Please put a bigger number if you get 'unsufficient unspent'
2014-11-06 09:53:09 -08:00
var fee = program . fee || 0.0001 ;
2014-11-03 04:55:44 -08:00
2014-10-30 06:38:14 -07:00
2014-11-03 03:45:38 -08:00
var totalCopayers = extPrivKeys . length ;
2014-10-30 06:38:14 -07:00
var addr = new bitcore . Address ( destAddr ) ;
2014-10-30 07:14:42 -07:00
if ( ! addr . isValid ( ) ) {
2014-10-30 06:38:14 -07:00
console . log ( '\tBad destination address' ) ; //TODO
process . exit ( 1 ) ;
2014-11-02 18:53:47 -08:00
2014-10-30 06:38:14 -07:00
}
2014-11-02 18:53:47 -08:00
2014-10-30 06:38:14 -07:00
var networkName = addr . network ( ) . name ;
2014-11-06 09:53:09 -08:00
console . log ( '\tNetwork: %s\n\tDestination Address:%s\n\tRequired copayers: %d\n\tTotal copayers: %d\n\tFee: %d\n\tKeys:' ,
networkName , destAddr , requiredCopayers ,
totalCopayers , fee , extPrivKeys ) ; //TODO
2014-10-30 07:14:42 -07:00
console . log ( '\n ----------------------------' ) ;
2014-10-30 06:38:14 -07:00
2014-11-01 05:42:56 -07:00
if ( requiredCopayers > totalCopayers )
throw new Error ( 'No enought private keys given' ) ;
2014-10-30 06:38:14 -07:00
2014-10-30 07:14:42 -07:00
function createWallet ( networkName , extPrivKeys , index ) {
2014-10-30 06:38:14 -07:00
2014-10-30 07:14:42 -07:00
var opts = { } ;
2014-10-30 06:38:14 -07:00
2014-10-30 07:14:42 -07:00
opts . networkName = networkName || 'testnet' ;
opts . publicKeyRing = new copay . PublicKeyRing ( {
networkName : networkName ,
requiredCopayers : requiredCopayers ,
totalCopayers : totalCopayers ,
} ) ;
2014-10-30 06:38:14 -07:00
2014-10-30 07:14:42 -07:00
_ . each ( extPrivKeys , function ( extPrivKey , i ) {
console . log ( '\tAdding key:' , i ) ;
2014-10-30 06:38:14 -07:00
2014-10-30 07:14:42 -07:00
var privateKey = new copay . PrivateKey ( {
networkName : networkName ,
extendedPrivateKeyString : extPrivKeys [ i ] ,
} ) ;
2014-10-30 06:38:14 -07:00
2014-10-30 07:14:42 -07:00
if ( i === index )
opts . privateKey = privateKey ;
opts . publicKeyRing . addCopayer (
privateKey . deriveBIP45Branch ( ) . extendedPublicKeyString ( ) ,
'public key ' + i
) ;
} )
console . log ( '\t### PublicKeyRing Initialized' ) ;
opts . txProposals = new copay . TxProposals ( {
2014-10-30 06:38:14 -07:00
networkName : networkName ,
} ) ;
2014-10-30 07:14:42 -07:00
opts . requiredCopayers = requiredCopayers ;
opts . totalCopayers = totalCopayers ;
opts . network = {
setHexNonce : sinon . stub ( ) ,
setHexNonces : sinon . stub ( ) ,
send : sinon . stub ( ) ,
} ;
// opts.networkOpts = {
// 'livenet': config.network.livenet,
// 'testnet': config.network.testnet,
// };
opts . blockchainOpts = {
'livenet' : config . network . livenet ,
'testnet' : config . network . testnet ,
} ;
opts . spendUnconfirmed = true ;
opts . version = version ;
// opts.reconnectDelay = opts.reconnectDelay || this.walletDefaults.reconnectDelay;
return new copay . Wallet ( opts ) ;
}
2014-10-30 06:38:14 -07:00
2014-10-30 07:14:42 -07:00
console . log ( '## CREATING ALL WALLETS' ) ; //TODO
var w = [ ] ;
_ . each ( extPrivKeys , function ( extPrivKey , i ) {
w . push ( createWallet ( networkName , extPrivKeys , i ) ) ;
2014-10-30 06:38:14 -07:00
} ) ;
2014-10-30 07:14:42 -07:00
console . log ( ' => %d Wallets created' , w . length ) ;
console . log ( '\n\n## Scanning for funds' ) ;
2014-10-30 06:38:14 -07:00
2014-10-30 07:14:42 -07:00
var firstWallet = w . pop ( ) ;
2014-10-30 06:38:14 -07:00
2014-10-30 07:14:42 -07:00
firstWallet . updateIndexes ( function ( ) {
2014-10-30 06:38:14 -07:00
console . log ( 'Scan done.' ) ; //TODO
2014-10-30 07:14:42 -07:00
firstWallet . getBalance ( function ( err , balance , balanceByAddr ) {
console . log ( '\n\n\n\n### TOTAL BALANCE: %d SATOSHIS' , balance ) ; //TODO
console . log ( 'Balance per address:' , balanceByAddr ) ; //TODO
2014-10-30 06:38:14 -07:00
2014-10-30 07:24:34 -07:00
if ( ! balance ) {
2014-11-03 03:40:53 -08:00
throw ( 'Could not find any coins in the generated wallet' ) ;
2014-10-30 07:24:34 -07:00
}
2014-11-02 18:53:47 -08:00
2014-11-06 09:53:09 -08:00
if ( amount && amount >= balance )
2014-11-03 03:40:53 -08:00
throw ( 'Not enought balance fund to fullfill ' + amount + ' satoshis' ) ;
2014-11-06 09:53:09 -08:00
rl . question ( "\n\tShould I swipe the wallet (destination address is:" + destAddr + " Amount: " + ( amount || balance ) + " satoshis)?\n\t(`yes` to continue)\n\t" , function ( answer ) {
if ( answer !== 'yes' )
process . exit ( 1 ) ;
2014-11-03 03:40:53 -08:00
amount = amount || balance - fee * bitcore . util . COIN ;
2014-10-30 07:24:34 -07:00
2014-12-22 11:59:37 -08:00
firstWallet . spend ( { toAddress : destAddr , amountSat : amount } , function ( err , ntxid ) {
2014-11-02 18:53:47 -08:00
if ( err || ! ntxid ) {
2014-11-03 03:40:53 -08:00
if ( err && err . toString ( ) . match ( 'BIG' ) ) {
2014-11-06 09:53:09 -08:00
throw new Error ( 'Could not create tx' + err ) ;
2014-11-03 03:40:53 -08:00
} else {
throw new Error ( 'Could not create tx' + err + '. Try a bigger fee (--fee).' ) ;
}
2014-11-02 18:53:47 -08:00
}
2014-11-01 12:16:44 -07:00
2014-11-02 18:53:47 -08:00
console . log ( '\n\t### Tx Proposal Created...\n\tWith copayer 0 signature.' ) ;
2014-11-01 12:16:44 -07:00
2014-11-02 15:32:12 -08:00
if ( requiredCopayers === 1 ) {
2014-10-30 12:45:44 -07:00
firstWallet . sendTx ( ntxid , function ( txid ) {
console . log ( '\t ####### SENT TXID:' , txid ) ;
process . exit ( 1 ) ;
} ) ;
}
2014-11-01 05:42:56 -07:00
var signers = w . slice ( 0 , requiredCopayers - 1 ) ;
console . log ( 'Will add %d more signatures:' , signers . length ) ;
var i = 0 ;
async . eachSeries ( signers , function ( dummy , cb ) {
console . log ( '\t Signing with copayer' , i + 1 ) ;
w [ i ] . txProposals = firstWallet . txProposals ;
2014-11-06 09:53:09 -08:00
try {
w [ i ] . sign ( ntxid ) ;
} catch ( e ) {
return cb ( 'Could not sign' ) ;
}
console . log ( '\t Signed!' ) ;
firstWallet . txProposals = firstWallet . txProposals ;
i ++ ;
return cb ( null ) ;
2014-11-01 05:42:56 -07:00
} ,
function ( err ) {
if ( err )
throw new Error ( err ) ;
2014-10-30 07:24:34 -07:00
2014-12-22 11:59:37 -08:00
var p = firstWallet . txProposals . get ( ntxid ) ;
2014-10-30 07:24:34 -07:00
if ( p . builder . isFullySigned ( ) ) {
console . log ( '\t FULLY SIGNED. BROADCASTING NOW....' ) ;
2014-11-02 15:32:12 -08:00
var tx = p . builder . build ( ) ;
var txHex = tx . serialize ( ) . toString ( 'hex' ) ;
2014-11-03 03:40:53 -08:00
//console.log('\t RAW TX: ', txHex);
2014-12-22 11:59:37 -08:00
firstWallet . broadcastToBitcoinNetwork ( ntxid , function ( txid ) {
2014-10-30 07:24:34 -07:00
console . log ( '\t ####### SENT TXID:' , txid ) ;
process . exit ( 1 ) ;
} ) ;
2014-11-01 05:42:56 -07:00
} else {
throw new Error ( 'Error: could not fully sign the TX' ) ;
2014-10-30 07:24:34 -07:00
}
2014-11-01 05:42:56 -07:00
}
)
2014-10-30 07:24:34 -07:00
} ) ;
2014-11-03 03:40:53 -08:00
} ) ;
2014-10-30 06:38:14 -07:00
} ) ;
} ) ;