Merge pull request #340 from matiu/feat/rawTx
add transaction raw HEX if tx status is "accepted"
This commit is contained in:
commit
3f1bae6f25
|
@ -260,7 +260,7 @@ TxProposal.prototype.addAction = function(copayerId, type, comment, signatures,
|
||||||
this._updateStatus();
|
this._updateStatus();
|
||||||
};
|
};
|
||||||
|
|
||||||
TxProposal.prototype._addSignaturesToBitcoreTx = function(t, signatures, xpub) {
|
TxProposal.prototype._addSignaturesToBitcoreTx = function(tx, signatures, xpub) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (signatures.length != this.inputs.length)
|
if (signatures.length != this.inputs.length)
|
||||||
|
@ -280,12 +280,12 @@ TxProposal.prototype._addSignaturesToBitcoreTx = function(t, signatures, xpub) {
|
||||||
sigtype: Bitcore.crypto.Signature.SIGHASH_ALL,
|
sigtype: Bitcore.crypto.Signature.SIGHASH_ALL,
|
||||||
publicKey: pub,
|
publicKey: pub,
|
||||||
};
|
};
|
||||||
t.inputs[i].addSignature(t, s);
|
tx.inputs[i].addSignature(tx, s);
|
||||||
i++;
|
i++;
|
||||||
} catch (e) {};
|
} catch (e) {};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (i != t.inputs.length)
|
if (i != tx.inputs.length)
|
||||||
throw new Error('Wrong signatures');
|
throw new Error('Wrong signatures');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -293,10 +293,14 @@ TxProposal.prototype._addSignaturesToBitcoreTx = function(t, signatures, xpub) {
|
||||||
TxProposal.prototype.sign = function(copayerId, signatures, xpub) {
|
TxProposal.prototype.sign = function(copayerId, signatures, xpub) {
|
||||||
try {
|
try {
|
||||||
// Tests signatures are OK
|
// Tests signatures are OK
|
||||||
var t = this.getBitcoreTx();
|
var tx = this.getBitcoreTx();
|
||||||
this._addSignaturesToBitcoreTx(t, signatures, xpub);
|
this._addSignaturesToBitcoreTx(tx, signatures, xpub);
|
||||||
|
|
||||||
this.addAction(copayerId, 'accept', null, signatures, xpub);
|
this.addAction(copayerId, 'accept', null, signatures, xpub);
|
||||||
|
|
||||||
|
if (this.status == 'accepted')
|
||||||
|
this.raw = tx.uncheckedSerialize();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -162,6 +162,10 @@ Storage.prototype._completeTxData = function(walletId, txs, cb) {
|
||||||
_.each(tx.actions, function(action) {
|
_.each(tx.actions, function(action) {
|
||||||
action.copayerName = wallet.getCopayer(action.copayerId).name;
|
action.copayerName = wallet.getCopayer(action.copayerId).name;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (tx.status=='accepted')
|
||||||
|
tx.raw = tx.getRawTx();
|
||||||
|
|
||||||
});
|
});
|
||||||
return cb(null, txs);
|
return cb(null, txs);
|
||||||
});
|
});
|
||||||
|
|
|
@ -2773,173 +2773,212 @@ describe('Wallet service', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#signTx', function() {
|
describe('#signTx', function() {
|
||||||
var server, wallet, txid;
|
describe('1-1', function() {
|
||||||
|
var server, wallet, txid;
|
||||||
|
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
helpers.createAndJoinWallet(2, 3, function(s, w) {
|
helpers.createAndJoinWallet(1, 1, function(s, w) {
|
||||||
server = s;
|
server = s;
|
||||||
wallet = w;
|
wallet = w;
|
||||||
helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
|
helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
|
||||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 20, null, TestData.copayers[0].privKey_1H_0);
|
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 20, null, TestData.copayers[0].privKey_1H_0);
|
||||||
server.createTx(txOpts, function(err, tx) {
|
server.createTx(txOpts, function(err, tx) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
should.exist(tx);
|
should.exist(tx);
|
||||||
txid = tx.id;
|
txid = tx.id;
|
||||||
done();
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('should sign a TX with multiple inputs, different paths', function(done) {
|
it('should sign a TX with multiple inputs, different paths, and return raw', function(done) {
|
||||||
server.getPendingTxs({}, function(err, txs) {
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
var tx = txs[0];
|
var tx = txs[0];
|
||||||
tx.id.should.equal(txid);
|
tx.id.should.equal(txid);
|
||||||
|
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
|
||||||
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
|
should.not.exist(tx.raw);
|
||||||
server.signTx({
|
server.signTx({
|
||||||
txProposalId: txid,
|
|
||||||
signatures: signatures,
|
|
||||||
}, function(err) {
|
|
||||||
should.not.exist(err);
|
|
||||||
server.getPendingTxs({}, function(err, txs) {
|
|
||||||
should.not.exist(err);
|
|
||||||
var tx = txs[0];
|
|
||||||
tx.id.should.equal(txid);
|
|
||||||
|
|
||||||
var actors = tx.getActors();
|
|
||||||
actors.length.should.equal(1);
|
|
||||||
actors[0].should.equal(wallet.copayers[0].id);
|
|
||||||
tx.getActionBy(wallet.copayers[0].id).type.should.equal('accept');
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should fail to sign with a xpriv from other copayer', function(done) {
|
|
||||||
server.getPendingTxs({}, function(err, txs) {
|
|
||||||
var tx = txs[0];
|
|
||||||
tx.id.should.equal(txid);
|
|
||||||
var signatures = helpers.clientSign(tx, TestData.copayers[1].xPrivKey);
|
|
||||||
server.signTx({
|
|
||||||
txProposalId: txid,
|
|
||||||
signatures: signatures,
|
|
||||||
}, function(err) {
|
|
||||||
err.code.should.equal('BAD_SIGNATURES');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail if one signature is broken', function(done) {
|
|
||||||
server.getPendingTxs({}, function(err, txs) {
|
|
||||||
var tx = txs[0];
|
|
||||||
tx.id.should.equal(txid);
|
|
||||||
|
|
||||||
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
|
|
||||||
signatures[0] = 1;
|
|
||||||
|
|
||||||
server.signTx({
|
|
||||||
txProposalId: txid,
|
|
||||||
signatures: signatures,
|
|
||||||
}, function(err) {
|
|
||||||
err.message.should.contain('signatures');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail on invalid signature', function(done) {
|
|
||||||
server.getPendingTxs({}, function(err, txs) {
|
|
||||||
var tx = txs[0];
|
|
||||||
tx.id.should.equal(txid);
|
|
||||||
|
|
||||||
var signatures = ['11', '22', '33', '44', '55'];
|
|
||||||
server.signTx({
|
|
||||||
txProposalId: txid,
|
|
||||||
signatures: signatures,
|
|
||||||
}, function(err) {
|
|
||||||
should.exist(err);
|
|
||||||
err.message.should.contain('Bad signatures');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail on wrong number of invalid signatures', function(done) {
|
|
||||||
server.getPendingTxs({}, function(err, txs) {
|
|
||||||
var tx = txs[0];
|
|
||||||
tx.id.should.equal(txid);
|
|
||||||
|
|
||||||
var signatures = _.take(helpers.clientSign(tx, TestData.copayers[0].xPrivKey), tx.inputs.length - 1);
|
|
||||||
server.signTx({
|
|
||||||
txProposalId: txid,
|
|
||||||
signatures: signatures,
|
|
||||||
}, function(err) {
|
|
||||||
should.exist(err);
|
|
||||||
err.message.should.contain('Bad signatures');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail when signing a TX previously rejected', function(done) {
|
|
||||||
server.getPendingTxs({}, function(err, txs) {
|
|
||||||
var tx = txs[0];
|
|
||||||
tx.id.should.equal(txid);
|
|
||||||
|
|
||||||
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
|
|
||||||
server.signTx({
|
|
||||||
txProposalId: txid,
|
|
||||||
signatures: signatures,
|
|
||||||
}, function(err) {
|
|
||||||
server.rejectTx({
|
|
||||||
txProposalId: txid,
|
txProposalId: txid,
|
||||||
|
signatures: signatures,
|
||||||
|
}, function(err, txp) {
|
||||||
|
should.not.exist(err);
|
||||||
|
txp.status.should.equal('accepted');
|
||||||
|
// The raw Tx should contain the Signatures.
|
||||||
|
txp.raw.should.contain(signatures[0]);
|
||||||
|
|
||||||
|
// Get pending should also contains the raw TX
|
||||||
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
|
var tx = txs[0];
|
||||||
|
should.not.exist(err);
|
||||||
|
tx.status.should.equal('accepted');
|
||||||
|
tx.raw.should.contain(signatures[0]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
describe('Multisign 2-3', function() {
|
||||||
|
var server, wallet, txid;
|
||||||
|
|
||||||
|
beforeEach(function(done) {
|
||||||
|
helpers.createAndJoinWallet(2, 3, function(s, w) {
|
||||||
|
server = s;
|
||||||
|
wallet = w;
|
||||||
|
helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
|
||||||
|
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 20, null, TestData.copayers[0].privKey_1H_0);
|
||||||
|
server.createTx(txOpts, function(err, tx) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(tx);
|
||||||
|
txid = tx.id;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sign a TX with multiple inputs, different paths', function(done) {
|
||||||
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
|
var tx = txs[0];
|
||||||
|
tx.id.should.equal(txid);
|
||||||
|
|
||||||
|
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
|
||||||
|
server.signTx({
|
||||||
|
txProposalId: txid,
|
||||||
|
signatures: signatures,
|
||||||
|
}, function(err, txp) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.not.exist(tx.raw);
|
||||||
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
|
should.not.exist(err);
|
||||||
|
var tx = txs[0];
|
||||||
|
tx.id.should.equal(txid);
|
||||||
|
|
||||||
|
var actors = tx.getActors();
|
||||||
|
actors.length.should.equal(1);
|
||||||
|
actors[0].should.equal(wallet.copayers[0].id);
|
||||||
|
tx.getActionBy(wallet.copayers[0].id).type.should.equal('accept');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to sign with a xpriv from other copayer', function(done) {
|
||||||
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
|
var tx = txs[0];
|
||||||
|
tx.id.should.equal(txid);
|
||||||
|
var signatures = helpers.clientSign(tx, TestData.copayers[1].xPrivKey);
|
||||||
|
server.signTx({
|
||||||
|
txProposalId: txid,
|
||||||
|
signatures: signatures,
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
err.code.should.contain('COPAYER_VOTED');
|
err.code.should.equal('BAD_SIGNATURES');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail when rejected a previously signed TX', function(done) {
|
it('should fail if one signature is broken', function(done) {
|
||||||
server.getPendingTxs({}, function(err, txs) {
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
var tx = txs[0];
|
var tx = txs[0];
|
||||||
tx.id.should.equal(txid);
|
tx.id.should.equal(txid);
|
||||||
|
|
||||||
|
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
|
||||||
|
signatures[0] = 1;
|
||||||
|
|
||||||
|
server.signTx({
|
||||||
|
txProposalId: txid,
|
||||||
|
signatures: signatures,
|
||||||
|
}, function(err) {
|
||||||
|
err.message.should.contain('signatures');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail on invalid signature', function(done) {
|
||||||
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
|
var tx = txs[0];
|
||||||
|
tx.id.should.equal(txid);
|
||||||
|
|
||||||
|
var signatures = ['11', '22', '33', '44', '55'];
|
||||||
|
server.signTx({
|
||||||
|
txProposalId: txid,
|
||||||
|
signatures: signatures,
|
||||||
|
}, function(err) {
|
||||||
|
should.exist(err);
|
||||||
|
err.message.should.contain('Bad signatures');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail on wrong number of invalid signatures', function(done) {
|
||||||
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
|
var tx = txs[0];
|
||||||
|
tx.id.should.equal(txid);
|
||||||
|
|
||||||
|
var signatures = _.take(helpers.clientSign(tx, TestData.copayers[0].xPrivKey), tx.inputs.length - 1);
|
||||||
|
server.signTx({
|
||||||
|
txProposalId: txid,
|
||||||
|
signatures: signatures,
|
||||||
|
}, function(err) {
|
||||||
|
should.exist(err);
|
||||||
|
err.message.should.contain('Bad signatures');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail when signing a TX previously rejected', function(done) {
|
||||||
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
|
var tx = txs[0];
|
||||||
|
tx.id.should.equal(txid);
|
||||||
|
|
||||||
server.rejectTx({
|
|
||||||
txProposalId: txid,
|
|
||||||
}, function(err) {
|
|
||||||
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
|
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
|
||||||
server.signTx({
|
server.signTx({
|
||||||
txProposalId: txid,
|
txProposalId: txid,
|
||||||
signatures: signatures,
|
signatures: signatures,
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
err.code.should.contain('COPAYER_VOTED');
|
server.rejectTx({
|
||||||
done();
|
txProposalId: txid,
|
||||||
|
}, function(err) {
|
||||||
|
err.code.should.contain('COPAYER_VOTED');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail to sign a non-pending TX', function(done) {
|
it('should fail when rejected a previously signed TX', function(done) {
|
||||||
async.waterfall([
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
|
var tx = txs[0];
|
||||||
|
tx.id.should.equal(txid);
|
||||||
|
|
||||||
function(next) {
|
|
||||||
server.rejectTx({
|
server.rejectTx({
|
||||||
txProposalId: txid,
|
txProposalId: txid,
|
||||||
reason: 'some reason',
|
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
should.not.exist(err);
|
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
|
||||||
next();
|
server.signTx({
|
||||||
|
txProposalId: txid,
|
||||||
|
signatures: signatures,
|
||||||
|
}, function(err) {
|
||||||
|
err.code.should.contain('COPAYER_VOTED');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
});
|
||||||
function(next) {
|
});
|
||||||
helpers.getAuthServer(wallet.copayers[1].id, function(server) {
|
|
||||||
|
it('should fail to sign a non-pending TX', function(done) {
|
||||||
|
async.waterfall([
|
||||||
|
|
||||||
|
function(next) {
|
||||||
server.rejectTx({
|
server.rejectTx({
|
||||||
txProposalId: txid,
|
txProposalId: txid,
|
||||||
reason: 'some reason',
|
reason: 'some reason',
|
||||||
|
@ -2947,34 +2986,45 @@ describe('Wallet service', function() {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
},
|
function(next) {
|
||||||
function(next) {
|
helpers.getAuthServer(wallet.copayers[1].id, function(server) {
|
||||||
server.getPendingTxs({}, function(err, txs) {
|
server.rejectTx({
|
||||||
should.not.exist(err);
|
|
||||||
txs.should.be.empty;
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function(next) {
|
|
||||||
helpers.getAuthServer(wallet.copayers[2].id, function(server) {
|
|
||||||
server.getTx({
|
|
||||||
txProposalId: txid
|
|
||||||
}, function(err, tx) {
|
|
||||||
should.not.exist(err);
|
|
||||||
var signatures = helpers.clientSign(tx, TestData.copayers[2].xPrivKey);
|
|
||||||
server.signTx({
|
|
||||||
txProposalId: txid,
|
txProposalId: txid,
|
||||||
signatures: signatures,
|
reason: 'some reason',
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
should.exist(err);
|
should.not.exist(err);
|
||||||
err.code.should.equal('TX_NOT_PENDING');
|
next();
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
},
|
function(next) {
|
||||||
]);
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
|
should.not.exist(err);
|
||||||
|
txs.should.be.empty;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
helpers.getAuthServer(wallet.copayers[2].id, function(server) {
|
||||||
|
server.getTx({
|
||||||
|
txProposalId: txid
|
||||||
|
}, function(err, tx) {
|
||||||
|
should.not.exist(err);
|
||||||
|
var signatures = helpers.clientSign(tx, TestData.copayers[2].xPrivKey);
|
||||||
|
server.signTx({
|
||||||
|
txProposalId: txid,
|
||||||
|
signatures: signatures,
|
||||||
|
}, function(err) {
|
||||||
|
should.exist(err);
|
||||||
|
err.code.should.equal('TX_NOT_PENDING');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3017,6 +3067,7 @@ describe('Wallet service', function() {
|
||||||
txProposalId: txpid
|
txProposalId: txpid
|
||||||
}, function(err, txp) {
|
}, function(err, txp) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
|
should.not.exist(txp.raw);
|
||||||
txp.txid.should.equal('999');
|
txp.txid.should.equal('999');
|
||||||
txp.isBroadcasted().should.be.true;
|
txp.isBroadcasted().should.be.true;
|
||||||
txp.broadcastedOn.should.equal(1234);
|
txp.broadcastedOn.should.equal(1234);
|
||||||
|
|
Loading…
Reference in New Issue