mirror of https://github.com/BTCPrivate/copay.git
TxProposal fully testes
This commit is contained in:
parent
2fe421b14f
commit
ae042a8292
|
@ -24,7 +24,7 @@ function TxProposal(opts) {
|
|||
this.builder = opts.builder;
|
||||
this.inputChainPaths = opts.inputChainPaths;
|
||||
|
||||
this._inputSignedBy = [];
|
||||
this._inputSignatures = [];
|
||||
this.seenBy = opts.seenBy || {};
|
||||
this.signedBy = opts.signedBy || {};
|
||||
this.rejectedBy = opts.rejectedBy || {};
|
||||
|
@ -69,25 +69,24 @@ TxProposal.fromObj = function(o, forceOpts) {
|
|||
};
|
||||
}
|
||||
var t = new TxProposal(o);
|
||||
t._throwIfInvalid();
|
||||
t._check();
|
||||
t._updateSignedBy();
|
||||
return t;
|
||||
};
|
||||
|
||||
|
||||
|
||||
TxProposal._formatKeys = function(allowedPubKeys) {
|
||||
var keys = [];
|
||||
for (var i in allowedPubKeys) {
|
||||
if (!Buffer.isBuffer(allowedPubKeys[i]))
|
||||
throw new Error('allowedPubKeys must be buffers');
|
||||
TxProposal._formatKeys = function(keys) {
|
||||
var ret = [];
|
||||
for (var i in keys) {
|
||||
if (!Buffer.isBuffer(keys[i]))
|
||||
throw new Error('keys must be buffers');
|
||||
|
||||
var k = new Key();
|
||||
k.public = allowedPubKeys[i];
|
||||
keys.push(k);
|
||||
k.public = keys[i];
|
||||
ret.push(k);
|
||||
};
|
||||
|
||||
return keys;
|
||||
return ret;
|
||||
};
|
||||
|
||||
TxProposal._verifySignatures = function(inKeys, scriptSig, txSigHash) {
|
||||
|
@ -107,13 +106,13 @@ TxProposal._verifySignatures = function(inKeys, scriptSig, txSigHash) {
|
|||
if (k.verifySignatureSync(txSigHash, sigRaw)) {
|
||||
ret.push(parseInt(j));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
TxProposal._keysFromRedeemScript = function(s) {
|
||||
TxProposal._infoFromRedeemScript = function(s) {
|
||||
var redeemScript = new Script(s.chunks[s.chunks.length - 1]);
|
||||
if (!redeemScript)
|
||||
throw new Error('Bad scriptSig');
|
||||
|
@ -121,71 +120,66 @@ TxProposal._keysFromRedeemScript = function(s) {
|
|||
if (!pubkeys || !pubkeys.length)
|
||||
throw new Error('Bad scriptSig');
|
||||
|
||||
return pubkeys;
|
||||
return {
|
||||
keys: pubkeys,
|
||||
scriptBuf: redeemScript.getBuffer()
|
||||
};
|
||||
};
|
||||
|
||||
TxProposal.prototype._updateSignedBy = function() {
|
||||
this._inputSignedBy = [];
|
||||
this._inputSignatures = [];
|
||||
|
||||
var tx = this.builder.build();
|
||||
for (var i in tx.ins) {
|
||||
var scriptSig = new Script(tx.ins[i].s);
|
||||
|
||||
var keys = TxProposal._keysFromRedeemScript(scriptSig);
|
||||
var txSigHash = tx.hashForSignature(this.builder.inputMap[i].scriptPubKey, i, Transaction.SIGHASH_ALL);
|
||||
var copayerIndex = this._verifySignatures(keys, scriptSig, txSigHash);
|
||||
if (typeof copayerIndex === 'undefined')
|
||||
var signatureCount = scriptSig.countSignatures();
|
||||
var info = TxProposal._infoFromRedeemScript(scriptSig);
|
||||
var txSigHash = tx.hashForSignature(info.scriptBuf, i, Transaction.SIGHASH_ALL);
|
||||
var signatureIndexes = TxProposal._verifySignatures(info.keys, scriptSig, txSigHash);
|
||||
if (signatureIndexes.length !== signatureCount)
|
||||
throw new Error('Invalid signature');
|
||||
this._inputSignedBy[i] = this._inputSignedBy[i] || {};
|
||||
this._inputSignedBy[i][copayerIndex] = true;
|
||||
this._inputSignatures[i] = signatureIndexes.map(function(i) {
|
||||
return info.keys[i].toString('hex');
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
TxProposal.prototype.isValid = function() {
|
||||
TxProposal.prototype._check = function() {
|
||||
|
||||
if (this.builder.signhash && this.builder.signhash !== Transaction.SIGHASH_ALL) {
|
||||
return false;
|
||||
throw new Error('Invalid tx proposal');
|
||||
}
|
||||
|
||||
var tx = this.builder.build();
|
||||
if (!tx.ins.length)
|
||||
return false;
|
||||
throw new Error('Invalid tx proposal: no ins');
|
||||
|
||||
var scriptSigs = this.builder.vanilla.scriptSigs;
|
||||
if (!scriptSigs || !scriptSigs.length) {
|
||||
throw new Error('Invalid tx proposal: no signatures');
|
||||
}
|
||||
|
||||
for (var i = 0; i < tx.ins.length; i++) {
|
||||
var hashType = tx.getHashType(i);
|
||||
if (hashType && hashType !== Transaction.SIGHASH_ALL) {
|
||||
return false;
|
||||
}
|
||||
if (hashType && hashType !== Transaction.SIGHASH_ALL)
|
||||
throw new Error('Invalid tx proposal: bad signatures');
|
||||
}
|
||||
|
||||
console.log('[TxProposal.js.145]'); //TODO
|
||||
|
||||
//Should be signed
|
||||
var scriptSigs = this.builder.vanilla.scriptSigs;
|
||||
if (!scriptSigs)
|
||||
return false;
|
||||
|
||||
|
||||
console.log('[TxProposal.js.153]'); //TODO
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
TxProposal.prototype._throwIfInvalid = function(allowedPubKeys) {
|
||||
if (!this.isValid(allowedPubKeys))
|
||||
throw new Error('Invalid tx proposal');
|
||||
TxProposal.prototype.mergeBuilder = function(incoming) {
|
||||
var b0 = this.builder;
|
||||
var b1 = incoming.builder;
|
||||
|
||||
var before = JSON.stringify(b0.toObj());
|
||||
b0.merge(b1);
|
||||
var after = JSON.stringify(b0.toObj());
|
||||
return after !== before;
|
||||
};
|
||||
|
||||
|
||||
TxProposal.prototype.merge = function(incoming, allowedPubKeys) {
|
||||
var ret = {};
|
||||
ret.events = [];
|
||||
incoming._throwIfInvalid(allowedPubKeys);
|
||||
|
||||
/* TODO */
|
||||
|
||||
/*
|
||||
events.push({
|
||||
/* OTDO
|
||||
events.push({
|
||||
type: 'seen',
|
||||
cId: k,
|
||||
txId: ntxid
|
||||
|
@ -202,29 +196,48 @@ txId: ntxid
|
|||
});
|
||||
ret.events = this.mergeMetadata(incoming);
|
||||
*/
|
||||
ret.hasChanged = this.mergeBuilder(incoming);
|
||||
|
||||
|
||||
TxProposal.prototype._allSignatures = function() {
|
||||
var ret = {};
|
||||
for(var i in this._inputSignatures)
|
||||
for (var j in this._inputSignatures[i])
|
||||
ret[this._inputSignatures[i][j]] = true;
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
TxProposal.prototype.mergeBuilder = function(incoming) {
|
||||
var b0 = this.builder;
|
||||
var b1 = incoming.builder;
|
||||
TxProposal.prototype.merge = function(incoming) {
|
||||
var ret = {};
|
||||
var newSignatures = [];
|
||||
|
||||
var before = JSON.stringify(b0.toObj());
|
||||
b0.merge(b1);
|
||||
var after = JSON.stringify(b0.toObj());
|
||||
return after !== before;
|
||||
incoming._check();
|
||||
incoming._updateSignedBy();
|
||||
|
||||
var prevInputSignatures = this._allSignatures();
|
||||
|
||||
ret.hasChanged = this.mergeBuilder(incoming);
|
||||
this._updateSignedBy();
|
||||
|
||||
if (ret.hasChanged)
|
||||
for(var i in this._inputSignatures)
|
||||
for (var j in this._inputSignatures[i])
|
||||
if (!prevInputSignatures[this._inputSignatures[i][j]])
|
||||
newSignatures.push(this._inputSignatures[i][j]);
|
||||
|
||||
ret.newSignatures = newSignatures;
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
//This should be on bitcore / Transaction
|
||||
TxProposal.prototype.countSignatures = function() {
|
||||
var tx = this.builder.build();
|
||||
|
||||
var ret = 0;
|
||||
for (var i in tx.ins) {
|
||||
ret += tx.countInputSignatures(i);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
module.exports = TxProposal;
|
||||
|
|
|
@ -2,16 +2,22 @@
|
|||
var bitcore = bitcore || require('bitcore');
|
||||
var Script = bitcore.Script;
|
||||
|
||||
var VALID_SCRIPTSIG_BUF = new Buffer('0048304502200708a381dde585ef7fdfaeaeb5da9b451d3e22b01eac8a5e3d03b959e24a7478022100c90e76e423523a54a9e9c43858337ebcef1a539a7fc685c2698dd8648fcf1b9101473044022030a77c9613d6ee010717c1abc494668d877e3fa0ae4c520f65cc3b308754c98c02205219d387bcb291bd44805b9468439e4168b02a6a180cdbcc24d84d71d696c1ae014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae','hex');
|
||||
|
||||
function Tx() {
|
||||
this.ins = [{s: new Buffer('0048304502200708a381dde585ef7fdfaeaeb5da9b451d3e22b01eac8a5e3d03b959e24a7478022100c90e76e423523a54a9e9c43858337ebcef1a539a7fc685c2698dd8648fcf1b9101473044022030a77c9613d6ee010717c1abc494668d877e3fa0ae4c520f65cc3b308754c98c02205219d387bcb291bd44805b9468439e4168b02a6a180cdbcc24d84d71d696c1ae014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae','hex')}];
|
||||
this.ins = [{s: VALID_SCRIPTSIG_BUF }];
|
||||
};
|
||||
|
||||
|
||||
Tx.prototype.getHashType = function() {
|
||||
return 1;
|
||||
};
|
||||
|
||||
Tx.prototype.getNormalizedHash = function() {
|
||||
return '123456';
|
||||
};
|
||||
Tx.prototype.hashForSignature = function() {
|
||||
return new Buffer('72403de587240b85855da2ebd29b7dab5d170a9662d4bf7c4980358b14091696','hex');
|
||||
return new Buffer('31103626e162f1cbfab6b95b08c9f6e78aae128523261cb37f8dfd4783cb09a7', 'hex');
|
||||
};
|
||||
|
||||
|
||||
|
@ -23,8 +29,16 @@ function FakeBuilder() {
|
|||
scriptPubKey: new Script(new Buffer('a914dc0623476aefb049066b09b0147a022e6eb8429187', 'hex')),
|
||||
scriptType: 4,
|
||||
i: 0 }];
|
||||
|
||||
this.vanilla = {
|
||||
scriptSigs: [VALID_SCRIPTSIG_BUF],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FakeBuilder.prototype.merge = function() {
|
||||
};
|
||||
|
||||
FakeBuilder.prototype.build = function() {
|
||||
return this.tx;
|
||||
};
|
||||
|
@ -33,5 +47,5 @@ FakeBuilder.prototype.build = function() {
|
|||
FakeBuilder.prototype.toObj = function() {
|
||||
return this;
|
||||
};
|
||||
|
||||
FakeBuilder.VALID_SCRIPTSIG_BUF = VALID_SCRIPTSIG_BUF;
|
||||
module.exports = FakeBuilder;
|
||||
|
|
|
@ -132,7 +132,7 @@ describe('TxProposal', function() {
|
|||
|
||||
|
||||
describe('Signature verification', function() {
|
||||
var validScriptSig = new bitcore.Script(new Buffer('00483045022100a35a5cbe37e39caa62bf1c347eae9c72be827c190b31494b184943b3012757a8022008a1ff72a34a5bf2fc955aa5b6f8a4c32cb0fab7e54c212a5f6f645bb95b8ef10149304602210092347916c3c3e6f1692bf9447b973779c28ce9985baaa3940b483af573f464b4022100ab91062796ab8acb32a0fa90e00627db5be77d9722400b3ecfd9c5f34a8092b1014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae', 'hex'));
|
||||
var validScriptSig = new bitcore.Script(FakeBuilder.VALID_SCRIPTSIG_BUF);
|
||||
|
||||
var pubkeys = [
|
||||
'03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d',
|
||||
|
@ -167,15 +167,86 @@ describe('TxProposal', function() {
|
|||
it('#_verifyScriptSig, two signatures', function() {
|
||||
// Data taken from bitcore's TransactionBuilder test
|
||||
var txp = dummyProposal;
|
||||
var ret = TxProposal._verifySignatures(pubkeys,validScriptSig, new Buffer('31103626e162f1cbfab6b95b08c9f6e78aae128523261cb37f8dfd4783cb09a7', 'hex'));
|
||||
var tx = dummyProposal.builder.build();
|
||||
var ret = TxProposal._verifySignatures(pubkeys,validScriptSig, tx.hashForSignature());
|
||||
ret.should.deep.equal([0, 3]);
|
||||
});
|
||||
it('#_keysFromRedeemScript', function() {
|
||||
var keys = TxProposal._keysFromRedeemScript(validScriptSig);
|
||||
it('#_infoFromRedeemScript', function() {
|
||||
var info = TxProposal._infoFromRedeemScript(validScriptSig);
|
||||
var keys = info.keys;
|
||||
keys.length.should.equal(5);
|
||||
for(var i in keys){
|
||||
keys[i].toString('hex').should.equal(pubkeys[i].toString('hex'));
|
||||
}
|
||||
Buffer.isBuffer(info.scriptBuf).should.equal(true);
|
||||
});
|
||||
it('#_updateSignedBy', function() {
|
||||
var txp = dummyProposal;
|
||||
txp._inputSignatures.should.deep.equal([]);
|
||||
txp._updateSignedBy();
|
||||
txp._inputSignatures.should.deep.equal([[ '03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3' ]]);
|
||||
});
|
||||
describe('#_check', function() {
|
||||
var txp = dummyProposal;
|
||||
var backup = txp.builder.tx.ins;
|
||||
|
||||
it('OK', function() {
|
||||
txp._check();
|
||||
});
|
||||
it('FAIL ins', function() {
|
||||
txp.builder.tx.ins = [];
|
||||
(function() { txp._check();} ).should.throw('no ins');
|
||||
txp.builder.tx.ins = backup;
|
||||
});
|
||||
it('FAIL signhash', function() {
|
||||
sinon.stub(txp.builder.tx,'getHashType').returns(2);
|
||||
(function() { txp._check();} ).should.throw('signatures');
|
||||
txp.builder.tx.getHashType.restore();
|
||||
});
|
||||
it('FAIL no signatures', function() {
|
||||
var backup = txp.builder.vanilla.scriptSigs;
|
||||
txp.builder.vanilla.scriptSigs = [];
|
||||
(function() { txp._check();} ).should.throw('no signatures');
|
||||
txp.builder.vanilla.scriptSigs = backup;
|
||||
});
|
||||
});
|
||||
describe('#merge', function() {
|
||||
var txp = dummyProposal;
|
||||
var backup = txp.builder.tx.ins;
|
||||
it('with self', function() {
|
||||
var ret = txp.merge(txp);
|
||||
ret.newSignatures.length.should.equal(0);
|
||||
ret.hasChanged.should.equal(false);
|
||||
});
|
||||
|
||||
it('with less signatures', function() {
|
||||
var backup = txp.builder.vanilla.scriptSigs[0];
|
||||
txp.builder.merge = function() {
|
||||
// 3 signatures.
|
||||
this.vanilla.scriptSigs=['0048304502207d8e832bd576c93300e53ab6cbd68641961bec60690c358fd42d8e42b7d7d687022100a1daa89923efdb4c9b615d065058d9e1644f67000694a7d0806759afa7bef19b014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae'];
|
||||
this.tx.ins[0].s=new Buffer(this.vanilla.scriptSigs[0],'hex');
|
||||
};
|
||||
var ret = txp.merge(txp);
|
||||
ret.hasChanged.should.equal(true);
|
||||
ret.newSignatures.length.should.equal(0);
|
||||
|
||||
txp.builder.vanilla.scriptSigs = [backup];
|
||||
txp.builder.tx.ins[0].s = new Buffer(backup,'hex');
|
||||
});
|
||||
|
||||
|
||||
it('with more signatures', function() {
|
||||
txp.builder.merge = function() {
|
||||
// 3 signatures.
|
||||
this.vanilla.scriptSigs=['00483045022100f75bd3eb92d8c9be9a94d848bbd1985fc0eaf4c47fb470a0b222881802a1f03802204eb239ae3082779b1ec4f2e69baa0362494071e707e1696c14ad23c8f2e184e20148304502201981482db0f369ce943293b6fec06a0347918663c766a79d4cbd0457801768d1022100aedf8d7c51d55a9ddbdcc0067ed6b648b77ce9660447bbcf4e2c209698efa0a30148304502203f0ddad47757f8705cb40e7c706590d2e2028a7027ffdb26dd208fd6155e0d28022100ccd206f9b969ab7f88ee4c5c6cee48c800a62dda024c5a8de7eb8612b833a0c0014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae'];
|
||||
this.tx.ins[0].s=new Buffer(this.vanilla.scriptSigs[0],'hex');
|
||||
};
|
||||
var ret = txp.merge(txp);
|
||||
ret.hasChanged.should.equal(true);
|
||||
ret.newSignatures.length.should.equal(1);
|
||||
ret.newSignatures[0].should.equal('0392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed03');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue