mirror of https://github.com/BTCPrivate/copay.git
Merge pull request #944 from maraoz/fix/txp-checks
Add txp checks and tests
This commit is contained in:
commit
2abffd3511
|
@ -5,10 +5,11 @@ var imports = require('soop').imports();
|
|||
var bitcore = require('bitcore');
|
||||
var util = bitcore.util;
|
||||
var Transaction = bitcore.Transaction;
|
||||
var Builder = bitcore.TransactionBuilder;
|
||||
var BuilderMockV0 = require('./BuilderMockV0');;
|
||||
var TransactionBuilder = bitcore.TransactionBuilder;
|
||||
var Script = bitcore.Script;
|
||||
var buffertools = bitcore.buffertools;
|
||||
var preconditions = require('preconditions').instance();
|
||||
|
||||
function TxProposal(opts) {
|
||||
this.creator = opts.creator;
|
||||
|
@ -44,7 +45,7 @@ TxProposal.prototype.setSent = function(sentTxid) {
|
|||
TxProposal.fromObj = function(o) {
|
||||
var t = new TxProposal(o);
|
||||
try {
|
||||
t.builder = new Builder.fromObj(o.builderObj);
|
||||
t.builder = new TransactionBuilder.fromObj(o.builderObj);
|
||||
} catch (e) {
|
||||
if (!o.version) {
|
||||
t.builder = new BuilderMockV0(o.builderObj);
|
||||
|
@ -54,6 +55,21 @@ TxProposal.fromObj = function(o) {
|
|||
return t;
|
||||
};
|
||||
|
||||
|
||||
TxProposal.prototype.isValid = function() {
|
||||
if (this.builder.signhash && this.builder.signhash !== Transaction.SIGHASH_ALL) {
|
||||
return false;
|
||||
}
|
||||
var tx = this.builder.build();
|
||||
for (var i = 0; i < tx.ins.length; i++) {
|
||||
var hashType = tx.getHashType(i);
|
||||
if (hashType && hashType !== Transaction.SIGHASH_ALL) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
TxProposal.getSentTs = function() {
|
||||
return this.sentTs;
|
||||
};
|
||||
|
@ -218,7 +234,6 @@ TxProposals.prototype.merge = function(inTxp, author) {
|
|||
return ret;
|
||||
};
|
||||
|
||||
var preconditions = require('preconditions').instance();
|
||||
TxProposals.prototype.add = function(data) {
|
||||
preconditions.checkArgument(data.inputChainPaths);
|
||||
preconditions.checkArgument(data.signedBy);
|
||||
|
|
|
@ -125,6 +125,15 @@ Wallet.prototype._handleTxProposal = function(senderId, data) {
|
|||
this.log('RECV TXPROPOSAL:', data);
|
||||
|
||||
var inTxp = TxProposals.TxProposal.fromObj(data.txProposal);
|
||||
var valid = inTxp.isValid();
|
||||
if (!valid) {
|
||||
var corruptEvent = {
|
||||
type: 'corrupt',
|
||||
cId: inTxp.creator
|
||||
};
|
||||
this.emit('txProposalEvent', corruptEvent);
|
||||
return;
|
||||
}
|
||||
var mergeInfo = this.txProposals.merge(inTxp, senderId);
|
||||
var added = this.addSeenToTxProposals();
|
||||
|
||||
|
|
|
@ -149,15 +149,17 @@ angular.module('copayApp.services')
|
|||
}, 3000);
|
||||
});
|
||||
w.on('txProposalEvent', function(e) {
|
||||
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
||||
switch (e.type) {
|
||||
case 'signed':
|
||||
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
||||
notification.info('Transaction Update', 'A transaction was signed by ' + user);
|
||||
break;
|
||||
case 'rejected':
|
||||
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
||||
notification.info('Transaction Update', 'A transaction was rejected by ' + user);
|
||||
break;
|
||||
case 'corrupt':
|
||||
notification.error('Transaction Error', 'Received corrupt transaction from '+user);
|
||||
break;
|
||||
}
|
||||
});
|
||||
w.on('addressBookUpdated', function(dontDigest) {
|
||||
|
|
|
@ -9,7 +9,7 @@ var WalletKey = bitcore.WalletKey;
|
|||
var Key = bitcore.Key;
|
||||
var bignum = bitcore.Bignum;
|
||||
var Script = bitcore.Script;
|
||||
var Builder = bitcore.TransactionBuilder;
|
||||
var TransactionBuilder = bitcore.TransactionBuilder;
|
||||
var util = bitcore.util;
|
||||
var networks = bitcore.networks;
|
||||
try {
|
||||
|
@ -151,7 +151,7 @@ describe('TxProposals model', function() {
|
|||
};
|
||||
};
|
||||
|
||||
var b = new Builder(opts)
|
||||
var b = new TransactionBuilder(opts)
|
||||
.setUnspent(utxos)
|
||||
.setOutputs([{
|
||||
address: toAddress,
|
||||
|
@ -628,5 +628,46 @@ describe('TxProposals model', function() {
|
|||
Object.keys(w2.txps).length.should.equal(1);
|
||||
});
|
||||
|
||||
describe('TxProposals model', function() {
|
||||
var createMockTxp = function(raw) {
|
||||
var tx = new Transaction();
|
||||
tx.parse(new Buffer(raw, 'hex'));
|
||||
var txb = new TransactionBuilder();
|
||||
var txp = new TxProposals.TxProposal({
|
||||
builder: txb
|
||||
});
|
||||
txb.build = function() {
|
||||
return tx;
|
||||
};
|
||||
return txp;
|
||||
};
|
||||
|
||||
it('should validate for no signatures yet in tx', function() {
|
||||
// taken from https://gist.github.com/gavinandresen/3966071
|
||||
var raw = '010000000189632848f99722915727c5c75da8db2dbf194342a0429828f66ff88fab2af7d60000000000ffffffff0140420f000000000017a914f815b036d9bbbce5e9f2a00abd1bf3dc91e955108700000000';
|
||||
var txp = createMockTxp(raw);
|
||||
txp.isValid().should.equal(true);
|
||||
});
|
||||
it('should validate for no signatures yet in copay generated tx', function() {
|
||||
// taken from copay incomplete tx proposal
|
||||
var raw = '0100000001e205297fd05e4504d72761dc7a16e5cc9f4ab89877f28aee97c1cc66b3f07d690100000000ffffffff01706f9800000000001976a91473707e88f79c9c616b44bc766a25efcb9f49346688ac00000000';
|
||||
var txp = createMockTxp(raw);
|
||||
txp.isValid().should.equal(true);
|
||||
});
|
||||
it('should validate for a SIGHASH_NONE tx in builder', function() {
|
||||
var raw = '010000000145c3bf51ced6cefaea8c6578a645316270dbf8600f46969d31136e1e06829598000000007000483045022100877c715e0f3bd6377086c96d4757b2c983682a1934d9e3f894941f4f1e18d4710220272ed81758d7a391ee4c15a29246f3fe75efbddeaf1118e4c0d3bb14f57cdba601255121022f58491a833933a9bea80d8e820e66bee91bd8c71bfa972fe70482360b48129951aeffffffff01706f9800000000001976a91408328947f0caf8728729d740cbecdfe3c2327db588ac00000000';
|
||||
var txp = createMockTxp(raw);
|
||||
txp.isValid().should.equal(true);
|
||||
});
|
||||
it('should not validate for a non SIGHASH_NONE tx in builder with 1 input', function() {
|
||||
var raw = '0100000001eaf08f93f895127fbf000128ac74f6e8c7f003854e5ee1f02a5fd820cb689beb00000000fdfe00004730440220778f3174393e9ee6b0bfa876b4150db6f12a4da9715044ead5e345c2781ceee002203aab31f1e1d3dcf77ca780d9af798139719891917c9a09123dba54483ef462bc02493046022100dd93b64b30580029605dbba09d7fa34194d9ff38fda0c4fa187c52bf7f79ae98022100dd7b056762087b9aa8ccfde328d7067fa1753b78c0ee25577122569ff9de1d57024c695221039f847c24f09d7299c10bba4e41b24dc78e47bbb05fd7c1d209c994899d6881062103d363476e634fc5cdc11e9330c05a141c1e0c7f8b616817bdb83e7579bbf870942103fb2072953ceab87c6da450ac661685a881ddb661002d2ec1d60bfd33e3ec807d53aeffffffff01d06bf5050000000017a914db682f579cf6ca483880460fcf4ab63e223dc07e8700000000';
|
||||
var txp = createMockTxp(raw);
|
||||
txp.isValid().should.equal(false);
|
||||
});
|
||||
it('should not validate for a non SIGHASH_NONE tx in builder with 1 input', function() {
|
||||
var raw = '0100000002d903852d223b3100fcc01e0b02d73a76a0787cdff7d000e9cba0e931917f407501000000fdfe0000493046022100b232e994fdca7fd61fcf8ffe4a7f746ff8f8baf2667ac80841de0250f521c402022100862c0783ca7eafcbd2786b9444ed6e83ae941dcc2248bea4db12b7815d15de050247304402200189fe0cde9d1dd192553f4dddb6764df3eb643f9f71be8aa015f41f2d4fd11f02205513b8ca985c3b5b936f814c7eba92e2e2985c83927ca06c41081d264c0be7a7024c695221026fa1a3ed0c820c1053c8ba101f3c96f85c55624a902a82cf6b2896ed5f9b3d1521035a3383c13dd346a5784adfe3ec3026ab31d519fdfae2740497b10bdfb994e6442103c7477a6668d5bc250fe727e358d951b9e05f1d7c02059bf59ecbb335f1eeec7953aeffffffffd903852d223b3100fcc01e0b02d73a76a0787cdff7d000e9cba0e931917f407500000000fdfd0000483045022100bdb9d14569af66d84af63416d77296ace24a96f1720d30e74bc6e316a4b3727502206ed54d532467393488889d72edbb667d075de491a89e8e496fee8791b943fa37024730440220379c30c884a21a949d8ec32d6934ffa9faf86add4d839de0f5fbd2b90f8ef1e802204048df2ec0035ce5e4bf01e9d70fd93a45a41ce2630100d692cd908cdaa61fc0024c69522102203938ef947327edce2cf2997c55b433be3d3ffcf3284c10d6fcdf4b01c6221f21033b60c3363a226ce9b850af655c6e1470d9a0936d7f56ea4a07ab84005f91cd1b210385755bc813fe7f92577b93bf689bf0d9b2118e6bbb7fee5d3d16976f4f7271af53aeffffffff01c02d9a3b0000000017a914db682f579cf6ca483880460fcf4ab63e223dc07e8700000000';
|
||||
var txp = createMockTxp(raw);
|
||||
txp.isValid().should.equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,6 +16,8 @@ var Network = require('./mocks/FakeNetwork');
|
|||
var Blockchain = require('./mocks/FakeBlockchain');
|
||||
var bitcore = bitcore || require('bitcore');
|
||||
var TransactionBuilder = bitcore.TransactionBuilder;
|
||||
var Transaction = bitcore.Transaction;
|
||||
var Address = bitcore.Address;
|
||||
|
||||
var addCopayers = function(w) {
|
||||
for (var i = 0; i < 4; i++) {
|
||||
|
@ -1011,4 +1013,80 @@ describe('Wallet model', function() {
|
|||
copayConfig.forceNetwork = backup;
|
||||
});
|
||||
});
|
||||
|
||||
describe('validate txProposals', function() {
|
||||
var a1 = 'n1pKARYYUnZwxBuGj3y7WqVDu6VLN7n971';
|
||||
var a2 = 'mtxYYJXZJmQc2iJRHQ4RZkfxU5K7TE2qMJ';
|
||||
var utxos = [{
|
||||
address: a1,
|
||||
txid: '2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1',
|
||||
vout: 1,
|
||||
scriptPubKey: Address.getScriptPubKeyFor(a1).serialize().toString('hex'),
|
||||
amount: 0.5,
|
||||
confirmations: 200
|
||||
}, {
|
||||
address: a2,
|
||||
txid: '88c4520ffd97ea565578afe0b40919120be704b36561c71ba4e450e83cb3c9fd',
|
||||
vout: 1,
|
||||
scriptPubKey: Address.getScriptPubKeyFor(a2).serialize().toString('hex'),
|
||||
amount: 0.5001,
|
||||
confirmations: 200
|
||||
}];
|
||||
var destAddress = 'myuAQcCc1REUgXGsCTiYhZvPPc3XxZ36G1';
|
||||
var outs = [{
|
||||
address: destAddress,
|
||||
amount: 1.0
|
||||
}];
|
||||
|
||||
var testValidate = function(signhash, result, done) {
|
||||
var w = cachedCreateW();
|
||||
var spy = sinon.spy();
|
||||
w.on('txProposalEvent', spy);
|
||||
w.on('txProposalEvent', function(e) {
|
||||
e.type.should.equal(result);
|
||||
done();
|
||||
});
|
||||
var opts = {};
|
||||
opts.signhash = signhash;
|
||||
var txb = new TransactionBuilder(opts)
|
||||
.setUnspent(utxos)
|
||||
.setOutputs(outs)
|
||||
.sign(['cVBtNonMyTydnS3NnZyipbduXo9KZfF1aUZ3uQHcvJB6UARZbiWG',
|
||||
'cRVF68hhZp1PUQCdjr2k6aVYb2cn6uabbySDPBizAJ3PXF7vDXTL'
|
||||
]);
|
||||
var txp = {
|
||||
'txProposal': {
|
||||
'builderObj': txb.toObj()
|
||||
}
|
||||
};
|
||||
w._handleTxProposal('senderID', txp, true);
|
||||
spy.callCount.should.equal(1);
|
||||
};
|
||||
|
||||
it('should validate for undefined', function(done) {
|
||||
var result = 'new';
|
||||
var signhash;
|
||||
testValidate(signhash, result, done);
|
||||
});
|
||||
it('should validate for SIGHASH_ALL', function(done) {
|
||||
var result = 'new';
|
||||
var signhash = Transaction.SIGHASH_ALL;
|
||||
testValidate(signhash, result, done);
|
||||
});
|
||||
it('should not validate for different SIGHASH_NONE', function(done) {
|
||||
var result = 'corrupt';
|
||||
var signhash = Transaction.SIGHASH_NONE;
|
||||
testValidate(signhash, result, done);
|
||||
});
|
||||
it('should not validate for different SIGHASH_SINGLE', function(done) {
|
||||
var result = 'corrupt';
|
||||
var signhash = Transaction.SIGHASH_SINGLE;
|
||||
testValidate(signhash, result, done);
|
||||
});
|
||||
it('should not validate for different SIGHASH_ANYONECANPAY', function(done) {
|
||||
var result = 'corrupt';
|
||||
var signhash = Transaction.SIGHASH_ANYONECANPAY;
|
||||
testValidate(signhash, result, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -180,25 +180,25 @@ describe("Unit: Testing Directives", function() {
|
|||
it('should check very weak password', function() {
|
||||
$scope.password = 'asd';
|
||||
$scope.$digest();
|
||||
expect($scope.passwordStrength).to.equal('very weak');
|
||||
expect($scope.passwordStrength).to.equal('Very Weak, that\'s short');
|
||||
});
|
||||
|
||||
it('should check weak password', function() {
|
||||
$scope.password = 'asdasdASDASD';
|
||||
$scope.$digest();
|
||||
expect($scope.passwordStrength).to.equal('weak');
|
||||
expect($scope.passwordStrength).to.equal('Weak, add numerals');
|
||||
});
|
||||
|
||||
it('should check medium password', function() {
|
||||
$scope.password = 'asdasdASDASD1';
|
||||
$scope.password = 'asdasdA1';
|
||||
$scope.$digest();
|
||||
expect($scope.passwordStrength).to.equal('medium');
|
||||
expect($scope.passwordStrength).to.equal('Medium, add punctuation');
|
||||
});
|
||||
|
||||
it('should check strong password', function() {
|
||||
$scope.password = 'asdasdASDASD1{';
|
||||
$scope.$digest();
|
||||
expect($scope.passwordStrength).to.equal('strong');
|
||||
expect($scope.passwordStrength).to.equal('Strong, add punctuation');
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue