Browse Source

Merge pull request #91 from isocolsky/sign_without_broadcast

Sign without broadcast
activeAddress
Matias Alejo Garcia 10 years ago
parent
commit
30000d010b
  1. 21
      lib/server.js
  2. 115
      test/integration/clientApi.js
  3. 215
      test/integration/server.js

21
lib/server.js

@ -695,24 +695,9 @@ WalletService.prototype.signTx = function(opts, cb) {
self._notify('TxProposalFinallyAccepted', {
txProposalId: opts.txProposalId,
});
self._broadcastTx(txp, function(err, txid) {
if (err) return cb(err);
txp.setBroadcasted(txid);
self.storage.storeTx(self.walletId, txp, function(err) {
if (err) return cb(err);
self._notify('NewOutgoingTx', {
txProposalId: opts.txProposalId,
txid: txid
});
return cb(null, txp);
});
});
} else {
return cb(null, txp);
}
return cb(null, txp);
});
});
});
@ -756,7 +741,7 @@ WalletService.prototype.broadcastTx = function(opts, cb) {
txid: txid
});
return cb(null, txid);
return cb(null, txp);
});
});
});

115
test/integration/clientApi.js

@ -722,7 +722,7 @@ describe('client API ', function() {
clients[0].signTxProposal(txp, function(err, txp) {
should.not.exist(err);
txp.status.should.equal('broadcasted');
txp.status.should.equal('accepted');
done();
});
});
@ -1141,17 +1141,20 @@ describe('client API ', function() {
toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5',
message: 'hello 1-1',
};
clients[0].sendTxProposal(opts, function(err, x) {
clients[0].sendTxProposal(opts, function(err, txp) {
should.not.exist(err);
x.requiredRejections.should.equal(1);
x.requiredSignatures.should.equal(1);
x.status.should.equal('pending');
x.changeAddress.path.should.equal('m/2147483647/1/0');
clients[0].signTxProposal(x, function(err, tx) {
txp.requiredRejections.should.equal(1);
txp.requiredSignatures.should.equal(1);
txp.status.should.equal('pending');
txp.changeAddress.path.should.equal('m/2147483647/1/0');
clients[0].signTxProposal(txp, function(err, txp) {
should.not.exist(err);
tx.status.should.equal('broadcasted');
tx.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id);
done();
txp.status.should.equal('accepted');
clients[0].broadcastTxProposal(txp, function(err, txp) {
txp.status.should.equal('broadcasted');
txp.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id);
done();
});
});
});
});
@ -1168,14 +1171,14 @@ describe('client API ', function() {
toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5',
message: 'hello 1-1',
};
clients[0].sendTxProposal(opts, function(err, x) {
clients[0].sendTxProposal(opts, function(err, txp) {
should.not.exist(err);
clients[0].getStatus(function(err, st) {
should.not.exist(err);
var x = st.pendingTxps[0];
x.status.should.equal('pending');
x.requiredRejections.should.equal(2);
x.requiredSignatures.should.equal(2);
var txp = st.pendingTxps[0];
txp.status.should.equal('pending');
txp.requiredRejections.should.equal(2);
txp.requiredSignatures.should.equal(2);
var w = st.wallet;
w.copayers.length.should.equal(3);
w.status.should.equal('complete');
@ -1184,14 +1187,17 @@ describe('client API ', function() {
b.lockedAmount.should.equal(1000000000);
clients[0].signTxProposal(x, function(err, tx) {
clients[0].signTxProposal(txp, function(err, txp) {
should.not.exist(err, err);
tx.status.should.equal('pending');
clients[1].signTxProposal(x, function(err, tx) {
txp.status.should.equal('pending');
clients[1].signTxProposal(txp, function(err, txp) {
should.not.exist(err);
tx.status.should.equal('broadcasted');
tx.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id);
done();
txp.status.should.equal('accepted');
clients[1].broadcastTxProposal(txp, function(err, txp) {
txp.status.should.equal('broadcasted');
txp.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id);
done();
});
});
});
});
@ -1211,21 +1217,24 @@ describe('client API ', function() {
toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5',
message: 'hello 1-1',
};
clients[0].sendTxProposal(opts, function(err, x) {
clients[0].sendTxProposal(opts, function(err, txp) {
should.not.exist(err);
x.status.should.equal('pending');
x.requiredRejections.should.equal(2);
x.requiredSignatures.should.equal(2);
clients[0].rejectTxProposal(x, 'wont sign', function(err, tx) {
txp.status.should.equal('pending');
txp.requiredRejections.should.equal(2);
txp.requiredSignatures.should.equal(2);
clients[0].rejectTxProposal(txp, 'wont sign', function(err, txp) {
should.not.exist(err, err);
tx.status.should.equal('pending');
clients[1].signTxProposal(x, function(err, tx) {
txp.status.should.equal('pending');
clients[1].signTxProposal(txp, function(err, txp) {
should.not.exist(err);
clients[2].signTxProposal(x, function(err, tx) {
clients[2].signTxProposal(txp, function(err, txp) {
should.not.exist(err);
tx.status.should.equal('broadcasted');
tx.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id);
done();
txp.status.should.equal('accepted');
clients[2].broadcastTxProposal(txp, function(err, txp) {
txp.status.should.equal('broadcasted');
txp.txid.should.equal((new Bitcore.Transaction(blockExplorerMock.lastBroadcasted)).id);
done();
});
});
});
});
@ -1245,21 +1254,21 @@ describe('client API ', function() {
toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5',
message: 'hello 1-1',
};
clients[0].sendTxProposal(opts, function(err, x) {
clients[0].sendTxProposal(opts, function(err, txp) {
should.not.exist(err);
x.status.should.equal('pending');
x.requiredRejections.should.equal(2);
x.requiredSignatures.should.equal(3);
txp.status.should.equal('pending');
txp.requiredRejections.should.equal(2);
txp.requiredSignatures.should.equal(3);
clients[0].rejectTxProposal(x, 'wont sign', function(err, tx) {
clients[0].rejectTxProposal(txp, 'wont sign', function(err, txp) {
should.not.exist(err, err);
tx.status.should.equal('pending');
clients[1].signTxProposal(x, function(err, tx) {
txp.status.should.equal('pending');
clients[1].signTxProposal(txp, function(err, txp) {
should.not.exist(err);
tx.status.should.equal('pending');
clients[2].rejectTxProposal(x, 'me neither', function(err, tx) {
txp.status.should.equal('pending');
clients[2].rejectTxProposal(txp, 'me neither', function(err, txp) {
should.not.exist(err);
tx.status.should.equal('rejected');
txp.status.should.equal('rejected');
done();
});
});
@ -1280,19 +1289,21 @@ describe('client API ', function() {
toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5',
message: 'hello 1-1',
};
clients[0].sendTxProposal(opts, function(err, x) {
clients[0].sendTxProposal(opts, function(err, txp) {
should.not.exist(err);
x.status.should.equal('pending');
x.requiredRejections.should.equal(2);
x.requiredSignatures.should.equal(2);
clients[0].signTxProposal(x, function(err, tx) {
should.not.exist(err, err);
tx.status.should.equal('pending');
clients[0].signTxProposal(x, function(err, tx) {
txp.status.should.equal('pending');
txp.requiredRejections.should.equal(2);
txp.requiredSignatures.should.equal(2);
clients[0].signTxProposal(txp, function(err, txp) {
should.not.exist(err);
txp.status.should.equal('pending');
clients[0].signTxProposal(txp, function(err) {
should.exist(err);
err.code.should.contain('CVOTED');
clients[1].rejectTxProposal(x, 'xx', function(err, tx) {
clients[1].rejectTxProposal(txp, 'xx', function(err, txp) {
should.not.exist(err);
clients[1].rejectTxProposal(x, 'xx', function(err, tx) {
clients[1].rejectTxProposal(txp, 'xx', function(err) {
should.exist(err);
err.code.should.contain('CVOTED');
done();
});

215
test/integration/server.js

@ -1085,85 +1085,6 @@ describe('Copay server', function() {
});
});
});
});
describe('#signTx and broadcast', function() {
var server, wallet;
beforeEach(function(done) {
helpers.createAndJoinWallet(1, 1, function(s, w) {
server = s;
wallet = w;
helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
done();
});
});
});
it('should sign and broadcast a tx', function(done) {
helpers.stubBroadcast('1122334455');
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, txp) {
should.not.exist(err);
txp.should.exist;
var txpid = txp.id;
server.getPendingTxs({}, function(err, txps) {
var txp = txps[0];
txp.id.should.equal(txpid);
var signatures = helpers.clientSign(txp, TestData.copayers[0].xPrivKey);
server.signTx({
txProposalId: txpid,
signatures: signatures,
}, function(err, txp) {
should.not.exist(err);
txp.status.should.equal('broadcasted');
txp.txid.should.equal('1122334455');
server.getTx({
id: txp.id
}, function(err, txp) {
txp.actions.length.should.equal(1);
txp.actions[0].copayerId.should.equal(wallet.copayers[0].id);
txp.actions[0].copayerName.should.equal(wallet.copayers[0].name);
done();
});
});
});
});
});
it('should keep tx as *accepted* if unable to broadcast it', function(done) {
helpers.stubBroadcastFail();
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, txp) {
should.not.exist(err);
txp.should.exist;
var txpid = txp.id;
server.getPendingTxs({}, function(err, txps) {
var txp = txps[0];
txp.id.should.equal(txpid);
var signatures = helpers.clientSign(txp, TestData.copayers[0].xPrivKey);
server.signTx({
txProposalId: txpid,
signatures: signatures,
}, function(err, txp) {
err.should.contain('broadcast');
server.getPendingTxs({}, function(err, txps) {
should.not.exist(err);
txps.length.should.equal(1);
var txp = txps[0];
txp.status.should.equal('accepted');
should.not.exist(txp.txid);
done();
});
});
});
});
});
});
describe('Tx proposal workflow', function() {
@ -1196,7 +1117,7 @@ describe('Copay server', function() {
});
});
it('tx proposals should not be broadcast until quorum is reached', function(done) {
it('tx proposals should not be finally accepted until quorum is reached', function(done) {
var txpId;
async.waterfall([
@ -1234,12 +1155,18 @@ describe('Copay server', function() {
txps.length.should.equal(1);
var txp = txps[0];
txp.isPending().should.be.true;
txp.isRejected().should.be.false;
txp.isAccepted().should.be.false;
txp.isRejected().should.be.false;
txp.isBroadcasted().should.be.false;
txp.actions.length.should.equal(1);
var action = txp.getActionBy(wallet.copayers[0].id);
action.type.should.equal('accept');
next(null, txp);
server.getNotifications({}, function(err, notifications) {
should.not.exist(err);
var last = _.last(notifications);
last.type.should.not.equal('TxProposalFinallyAccepted');
next(null, txp);
});
});
},
function(txp, next) {
@ -1257,22 +1184,20 @@ describe('Copay server', function() {
function(next) {
server.getPendingTxs({}, function(err, txps) {
should.not.exist(err);
txps.length.should.equal(0);
next();
});
},
function(next) {
server.getTx({
id: txpId
}, function(err, txp) {
should.not.exist(err);
txp.isPending().should.be.false;
txp.isRejected().should.be.false;
txps.length.should.equal(1);
var txp = txps[0];
txp.isPending().should.be.true;
txp.isAccepted().should.be.true;
txp.isBroadcasted().should.be.true;
txp.txid.should.equal('999');
txp.isBroadcasted().should.be.false;
should.not.exist(txp.txid);
txp.actions.length.should.equal(2);
done();
server.getNotifications({}, function(err, notifications) {
should.not.exist(err);
var last = _.last(notifications);
last.type.should.equal('TxProposalFinallyAccepted');
last.data.txProposalId.should.equal(txp.id);
done();
});
});
},
]);
@ -1553,25 +1478,31 @@ describe('Copay server', function() {
server.getPendingTxs({}, function(err, txs) {
var tx = txs[2];
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
helpers.stubBroadcast('1122334455');
sinon.spy(server, 'emit');
server.signTx({
txProposalId: tx.id,
signatures: signatures,
}, function(err) {
server.getNotifications({
limit: 3,
reverse: true,
}, function(err, notifications) {
should.not.exist(err);
helpers.stubBroadcast('1122334455');
server.broadcastTx({
txProposalId: tx.id
}, function(err, txp) {
should.not.exist(err);
var types = _.pluck(notifications, 'type');
types.should.deep.equal(['NewOutgoingTx', 'TxProposalFinallyAccepted', 'TxProposalAcceptedBy']);
// Check also events
server.emit.getCall(0).args[0].type.should.equal('TxProposalAcceptedBy');
server.emit.getCall(1).args[0].type.should.equal('TxProposalFinallyAccepted');;
server.emit.getCall(2).args[0].type.should.equal('NewOutgoingTx');
server.getNotifications({
limit: 3,
reverse: true,
}, function(err, notifications) {
should.not.exist(err);
var types = _.pluck(notifications, 'type');
types.should.deep.equal(['NewOutgoingTx', 'TxProposalFinallyAccepted', 'TxProposalAcceptedBy']);
// Check also events
server.emit.getCall(0).args[0].type.should.equal('TxProposalAcceptedBy');
server.emit.getCall(1).args[0].type.should.equal('TxProposalFinallyAccepted');;
server.emit.getCall(2).args[0].type.should.equal('NewOutgoingTx');
done();
done();
});
});
});
});
@ -1860,44 +1791,50 @@ describe('Copay server', function() {
should.not.exist(err);
should.exist(tx);
helpers.stubBroadcast('1122334455');
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
server.signTx({
txProposalId: tx.id,
signatures: signatures,
}, function(err, tx) {
should.not.exist(err);
var txs = [{
txid: '1122334455',
confirmations: 1,
fees: 5460,
minedTs: 1,
inputs: [{
address: tx.inputs[0].address,
amount: utxos[0].satoshis,
}],
outputs: [{
address: 'external',
amount: helpers.toSatoshi(80) - 5460,
}, {
address: changeAddresses[0].address,
amount: helpers.toSatoshi(20) - 5460,
}],
}];
helpers.stubHistory(txs);
server.getTxHistory({}, function(err, txs) {
helpers.stubBroadcast('1122334455');
server.broadcastTx({
txProposalId: tx.id
}, function(err, txp) {
should.not.exist(err);
should.exist(txs);
txs.length.should.equal(1);
var tx = txs[0];
tx.action.should.equal('sent');
tx.amount.should.equal(helpers.toSatoshi(80));
tx.message.should.equal('some message');
tx.actions.length.should.equal(1);
tx.actions[0].type.should.equal('accept');
tx.actions[0].copayerName.should.equal('copayer 1');
done();
var txs = [{
txid: '1122334455',
confirmations: 1,
fees: 5460,
minedTs: 1,
inputs: [{
address: tx.inputs[0].address,
amount: utxos[0].satoshis,
}],
outputs: [{
address: 'external',
amount: helpers.toSatoshi(80) - 5460,
}, {
address: changeAddresses[0].address,
amount: helpers.toSatoshi(20) - 5460,
}],
}];
helpers.stubHistory(txs);
server.getTxHistory({}, function(err, txs) {
should.not.exist(err);
should.exist(txs);
txs.length.should.equal(1);
var tx = txs[0];
tx.action.should.equal('sent');
tx.amount.should.equal(helpers.toSatoshi(80));
tx.message.should.equal('some message');
tx.actions.length.should.equal(1);
tx.actions[0].type.should.equal('accept');
tx.actions[0].copayerName.should.equal('copayer 1');
done();
});
});
});
});

Loading…
Cancel
Save