Merge pull request #10 from matiu/feat/createTx3

implements createTx
This commit is contained in:
Ivan Socolsky 2015-02-03 23:33:00 -03:00
commit 7e5a8dc16d
8 changed files with 189 additions and 32 deletions

View File

@ -29,7 +29,7 @@ Address.fromObj = function (obj) {
* @return {Script}
*/
Address.prototype.getScriptPubKey = function (threshold) {
return Bitcore.Script.buildMultisigOut(this.publicKeys, threshold)
return Bitcore.Script.buildMultisigOut(this.publicKeys, threshold).toScriptHashOut();
};
module.exports = Address;

View File

@ -1,6 +1,7 @@
'use strict';
var _ = require('lodash');
var Guid = require('guid');
var TxProposalAction = require('./txproposalaction');
@ -11,7 +12,7 @@ function TxProposal(opts) {
this.version = VERSION;
this.createdOn = Math.floor(Date.now() / 1000);
this.id = opts.id;
this.id = Guid.raw();
this.creatorId = opts.creatorId;
this.toAddress = opts.toAddress;
this.amount = opts.amount;

View File

@ -113,6 +113,7 @@ Wallet.prototype.createAddress = function(isChange) {
return new Address({
address: bitcoreAddress.toString(),
path: path,
publicKeys: _.invoke(publicKeys, 'toString'),
});
};

View File

@ -299,6 +299,8 @@ CopayServer.prototype._getUtxos = function(opts, cb) {
dictionary[input].locked = true;
}
});
return cb(null, utxos);
});
});
@ -323,14 +325,15 @@ CopayServer.prototype.getBalance = function(opts, cb) {
if (err) return cb(err);
var balance = {};
balance.totalAmount = _.reduce(utxos, function(sum, utxo) {
balance.totalAmount = Utils.strip(_.reduce(utxos, function(sum, utxo) {
return sum + utxo.amount;
}, 0);
balance.lockedAmount = _.reduce(_.filter(utxos, {
}, 0));
balance.lockedAmount = Utils.strip(_.reduce(_.filter(utxos, {
locked: true
}), function(sum, utxo) {
return sum + utxo.amount;
}, 0);
}, 0));
return cb(null, balance);
});
@ -338,12 +341,15 @@ CopayServer.prototype.getBalance = function(opts, cb) {
CopayServer.prototype._createRawTx = function(txp) {
console.log(txp);
console.log('[server.js.320:txp:]',txp.inputs, txp.toAddress, txp.amount, txp.changeAddress); //TODO
var rawTx = new Bitcore.Transaction()
.from(txp.inputs)
.to(txp.toAddress, txp.amount)
.change(txp.changeAddress);
console.log('[server.js.324:rawTx:]',rawTx); //TODO
return rawTx;
};
@ -361,7 +367,7 @@ CopayServer.prototype._selectUtxos = function(txp, utxos) {
}
i++;
};
return selected;
return total >= txp.amount ? selected : null;
};
@ -380,6 +386,11 @@ CopayServer.prototype.createTx = function(opts, cb) {
Utils.checkRequired(opts, ['walletId', 'copayerId', 'toAddress', 'amount', 'message']);
// TODO?
// Check some parameters like:
// amount > dust
self.getWallet({
id: opts.walletId
}, function(err, wallet) {
@ -390,7 +401,9 @@ CopayServer.prototype.createTx = function(opts, cb) {
}, function(err, utxos) {
if (err) return cb(err);
utxos = _.without(utxos, {
var changeAddress = wallet.createAddress(true).address;
utxos = _.reject(utxos, {
locked: true
});
@ -398,14 +411,17 @@ CopayServer.prototype.createTx = function(opts, cb) {
creatorId: opts.copayerId,
toAddress: opts.toAddress,
amount: opts.amount,
changeAddress: opts.changeAddress,
changeAddress: changeAddress,
requiredSignatures: wallet.m,
maxRejections: wallet.n - wallet.m,
});
txp.inputs = self._selectUtxos(txp, utxos);
if (!txp.inputs)
return cb('insufficient funds')
txp.rawTx = self._createRawTx(txp);
// no need to do this now: // TODO remove this comment
//self._createRawTx(txp);
self.storage.storeTx(wallet.id, txp, function(err) {
if (err) return cb(err);

View File

@ -59,7 +59,7 @@ Storage.prototype.fetchTxs = function (walletId, cb) {
};
Storage.prototype.storeTx = function (walletId, txp, cb) {
this.db.put('wallet-' + walletId + '-txp-' + txp.txProposalId, txp, cb);
this.db.put('wallet-' + walletId + '-txp-' + txp.id, txp, cb);
};
Storage.prototype.fetchAddresses = function (walletId, cb) {

View File

@ -25,4 +25,14 @@ Utils.checkRequired = function (obj, args) {
});
};
/**
*
* @desc rounds a JAvascript number
* @param number
* @return {number}
*/
Utils.strip = function(number) {
return (parseFloat(number.toPrecision(12)));
}
module.exports = Utils;

View File

@ -26,7 +26,8 @@
"levelup": "^0.19.0",
"lodash": "^2.4.1",
"npmlog": "^0.1.1",
"preconditions": "^1.0.7"
"preconditions": "^1.0.7",
"guid":"*"
},
"devDependencies": {
"chai": "^1.9.1",

View File

@ -97,11 +97,7 @@ helpers.createAndJoinWallet = function(id, m, n, cb) {
};
helpers.randomTXID = function() {
var ret = '';
for (var i = 0; i < 64 / 4; i++)
ret += Math.floor(Math.random() * 255 * 255).toString(16);
return ret;
return Bitcore.crypto.Hash.sha256(new Buffer(Math.random() * 100000)).toString('hex');;
};
helpers.createUtxos = function(server, wallet, amounts, cb) {
@ -113,7 +109,6 @@ helpers.createUtxos = function(server, wallet, amounts, cb) {
isChange: false,
}, function(err, address) {
addresses.push(address);
console.log('[integration.js.115:address:]', address); //TODO
next(err);
});
},
@ -126,7 +121,7 @@ helpers.createUtxos = function(server, wallet, amounts, cb) {
txid: helpers.randomTXID(),
vout: Math.floor((Math.random() * 10) + 1),
amount: amount,
scriptPubKey: addresses[i].getScriptPubKey(wallet.m),
scriptPubKey: addresses[i].getScriptPubKey(wallet.m).toBuffer().toString('hex'),
};
}));
});
@ -623,12 +618,12 @@ describe('Copay server', function() {
it('should create many addresses on simultaneous requests', function(done) {
helpers.createAndJoinWallet('123', 2, 2, function(err, wallet) {
async.map(_.range(10), function (i, cb) {
async.map(_.range(10), function(i, cb) {
server.createAddress({
walletId: '123',
isChange: false,
}, cb);
}, function (err, addresses) {
}, function(err, addresses) {
addresses.length.should.equal(10);
addresses[0].path.should.equal('m/2147483647/0/0');
addresses[9].path.should.equal('m/2147483647/0/9');
@ -721,30 +716,27 @@ describe('Copay server', function() {
});
});
it.skip('should create tx', function(done) {
it('should create tx', function(done) {
helpers.createUtxos(server, wallet, [100, 200], function(utxos) {
console.log('[integration.js.670:utxos:]', utxos); //TODO
var bc = sinon.stub();
bc.getUnspentUtxos = sinon.stub().callsArgWith(1, null, utxos);
server._getBlockExplorer = sinon.stub().returns(bc);
//server._createRawTx = sinon.stub().returns('raw');
var txOpts = {
copayerId: '1',
walletId: '123',
toAddress: 'dummy',
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
amount: 80,
message: 'some message',
otToken: 'dummy',
requestSignature: 'dummy',
};
server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
tx.should.exist;
console.log(tx);
//tx.rawTx.should.equal('raw');
tx.isAccepted().should.equal.false;
tx.isRejected().should.equal.false;
server.getPendingTxs({
@ -765,10 +757,146 @@ describe('Copay server', function() {
});
});
it.skip('should fail to create tx when insufficient funds', function(done) {});
it('should fail to create tx when insufficient funds', function(done) {
it.skip('should create tx when there is a pending tx and enough UTXOs', function(done) {});
helpers.createUtxos(server, wallet, [100], function(utxos) {
it.skip('should fail to create tx when there is a pending tx and not enough UTXOs', function(done) {});
var bc = sinon.stub();
bc.getUnspentUtxos = sinon.stub().callsArgWith(1, null, utxos);
server._getBlockExplorer = sinon.stub().returns(bc);
var txOpts = {
copayerId: '1',
walletId: '123',
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
amount: 120,
message: 'some message',
otToken: 'dummy',
requestSignature: 'dummy',
};
server.createTx(txOpts, function(err, tx) {
err.should.contain('insufficient');
server.getPendingTxs({
walletId: '123'
}, function(err, txs) {
should.not.exist(err);
txs.length.should.equal(0);
server.getBalance({
walletId: '123'
}, function(err, balance) {
should.not.exist(err);
balance.lockedAmount.should.equal(0);
balance.totalAmount.should.equal(100);
done();
});
});
});
});
});
it('should create tx when there is a pending tx and enough UTXOs', function(done) {
helpers.createUtxos(server, wallet, [10.1, 10.2, 10.3], function(utxos) {
var bc = sinon.stub();
bc.getUnspentUtxos = sinon.stub().callsArgWith(1, null, utxos);
server._getBlockExplorer = sinon.stub().returns(bc);
var txOpts = {
copayerId: '1',
walletId: '123',
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
amount: 12,
message: 'some message',
otToken: 'dummy',
requestSignature: 'dummy',
};
server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
tx.should.exist;
var txOpts2 = {
copayerId: '1',
walletId: '123',
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
amount: 8,
message: 'some message 2',
otToken: 'dummy',
requestSignature: 'dummy',
};
server.createTx(txOpts2, function(err, tx) {
should.not.exist(err);
tx.should.exist;
server.getPendingTxs({
walletId: '123'
}, function(err, txs) {
should.not.exist(err);
txs.length.should.equal(2);
server.getBalance({
walletId: '123'
}, function(err, balance) {
should.not.exist(err);
balance.totalAmount.should.equal(30.6);
balance.lockedAmount.should.equal(30.6);
done();
});
});
});
});
});
});
it('should fail to create tx when there is a pending tx and not enough UTXOs', function(done) {
helpers.createUtxos(server, wallet, [10.1, 10.2, 10.3], function(utxos) {
var bc = sinon.stub();
bc.getUnspentUtxos = sinon.stub().callsArgWith(1, null, utxos);
server._getBlockExplorer = sinon.stub().returns(bc);
var txOpts = {
copayerId: '1',
walletId: '123',
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
amount: 12,
message: 'some message',
otToken: 'dummy',
requestSignature: 'dummy',
};
server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
tx.should.exist;
var txOpts2 = {
copayerId: '1',
walletId: '123',
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
amount: 24,
message: 'some message 2',
otToken: 'dummy',
requestSignature: 'dummy',
};
server.createTx(txOpts2, function(err, tx) {
err.should.contain('insufficient');
should.not.exist(tx);
server.getPendingTxs({
walletId: '123'
}, function(err, txs) {
should.not.exist(err);
txs.length.should.equal(1);
server.getBalance({
walletId: '123'
}, function(err, balance) {
should.not.exist(err);
balance.totalAmount.should.equal(30.6);
balance.lockedAmount.should.equal(20.3);
done();
});
});
});
});
});
});
});
});