mirror of https://github.com/BTCPrivate/copay.git
226 lines
6.6 KiB
JavaScript
Executable File
226 lines
6.6 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
'use strict';
|
|
|
|
|
|
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');
|
|
var async = require('async');
|
|
var program = require('commander');
|
|
|
|
function list(val) {
|
|
return val.split(',');
|
|
}
|
|
|
|
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)
|
|
.option('-k, --keys <items>', 'master private keys, separated by , ', list)
|
|
.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)
|
|
.parse(process.argv);
|
|
|
|
|
|
var rl = readline.createInterface({
|
|
input: process.stdin,
|
|
output: process.stdout
|
|
});
|
|
var args = process.argv;
|
|
|
|
var requiredCopayers = program.required;
|
|
var extPrivKeys = program.keys;
|
|
var destAddr = program.destination;
|
|
var amount = program.amount;
|
|
|
|
if (amount && program.fee) {
|
|
console.log('If amount if given, fee will be automatically calculated');
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!requiredCopayers || !extPrivKeys || !extPrivKeys.length || !destAddr) {
|
|
program.outputHelp();
|
|
process.exit(1);
|
|
}
|
|
|
|
// Fee to asign to the tx. Please put a bigger number if you get 'unsufficient unspent'
|
|
var fee = program.fee || 0.0001;
|
|
|
|
|
|
var totalCopayers = extPrivKeys.length;
|
|
var addr = new bitcore.Address(destAddr);
|
|
if (!addr.isValid()) {
|
|
console.log('\tBad destination address'); //TODO
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
var networkName = addr.network().name;
|
|
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
|
|
console.log('\n ----------------------------');
|
|
|
|
if (requiredCopayers > totalCopayers)
|
|
throw new Error('No enought private keys given');
|
|
|
|
|
|
function createWallet(networkName, extPrivKeys, index) {
|
|
|
|
var opts = {};
|
|
|
|
opts.networkName = networkName || 'testnet';
|
|
opts.publicKeyRing = new copay.PublicKeyRing({
|
|
networkName: networkName,
|
|
requiredCopayers: requiredCopayers,
|
|
totalCopayers: totalCopayers,
|
|
});
|
|
|
|
|
|
_.each(extPrivKeys, function(extPrivKey, i) {
|
|
console.log('\tAdding key:', i);
|
|
|
|
var privateKey = new copay.PrivateKey({
|
|
networkName: networkName,
|
|
extendedPrivateKeyString: extPrivKeys[i],
|
|
});
|
|
|
|
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({
|
|
networkName: networkName,
|
|
});
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
console.log('## CREATING ALL WALLETS'); //TODO
|
|
var w = [];
|
|
_.each(extPrivKeys, function(extPrivKey, i) {
|
|
w.push(createWallet(networkName, extPrivKeys, i));
|
|
});
|
|
console.log(' => %d Wallets created', w.length);
|
|
|
|
console.log('\n\n## Scanning for funds');
|
|
|
|
var firstWallet = w.pop();
|
|
|
|
firstWallet.updateIndexes(function() {
|
|
console.log('Scan done.'); //TODO
|
|
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
|
|
|
|
if (!balance) {
|
|
throw ('Could not find any coins in the generated wallet');
|
|
}
|
|
|
|
|
|
if (amount && amount >= balance)
|
|
throw ('Not enought balance fund to fullfill ' + amount + ' satoshis');
|
|
|
|
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);
|
|
|
|
amount = amount || balance - fee * bitcore.util.COIN;
|
|
|
|
firstWallet.spend({ toAddress: destAddr, amountSat: amount }, function(err, ntxid) {
|
|
if (err || !ntxid) {
|
|
if (err && err.toString().match('BIG')) {
|
|
throw new Error('Could not create tx' + err);
|
|
} else {
|
|
throw new Error('Could not create tx' + err + '. Try a bigger fee (--fee).');
|
|
}
|
|
}
|
|
|
|
console.log('\n\t### Tx Proposal Created...\n\tWith copayer 0 signature.');
|
|
|
|
if (requiredCopayers === 1) {
|
|
firstWallet.sendTx(ntxid, function(txid) {
|
|
console.log('\t ####### SENT TXID:', txid);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
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;
|
|
|
|
try {
|
|
w[i].sign(ntxid);
|
|
} catch (e) {
|
|
return cb('Could not sign');
|
|
}
|
|
|
|
console.log('\t Signed!');
|
|
firstWallet.txProposals = firstWallet.txProposals;
|
|
i++;
|
|
return cb(null);
|
|
},
|
|
function(err) {
|
|
if (err)
|
|
throw new Error(err);
|
|
|
|
var p = firstWallet.txProposals.get(ntxid);
|
|
if (p.builder.isFullySigned()) {
|
|
console.log('\t FULLY SIGNED. BROADCASTING NOW....');
|
|
var tx = p.builder.build();
|
|
var txHex = tx.serialize().toString('hex');
|
|
//console.log('\t RAW TX: ', txHex);
|
|
firstWallet.broadcastToBitcoinNetwork(ntxid, function(txid) {
|
|
console.log('\t ####### SENT TXID:', txid);
|
|
process.exit(1);
|
|
});
|
|
} else {
|
|
throw new Error('Error: could not fully sign the TX');
|
|
}
|
|
}
|
|
)
|
|
});
|
|
});
|
|
});
|
|
});
|