txp* test passing

This commit is contained in:
Matias Alejo Garcia 2014-08-03 22:34:47 -03:00
commit 753b890658
5 changed files with 417 additions and 193 deletions

View file

@ -297,9 +297,10 @@ PublicKeyRing.prototype.forPaths = function(paths) {
};
// returns pubkey -> copayerId.
PublicKeyRing.prototype.copayersForPubkeys = function(pubkeys, paths) {
var inKeyMap = {}, ret = [];
var inKeyMap = {}, ret = {};
for(var i in pubkeys ){
inKeyMap[pubkeys[i]] = 1;
};
@ -309,7 +310,7 @@ PublicKeyRing.prototype.copayersForPubkeys = function(pubkeys, paths) {
for(var copayerIndex in keys[i] ){
var kHex = keys[i][copayerIndex].toString('hex');
if (inKeyMap[kHex]) {
ret.push(this.copayerIds[copayerIndex]);
ret[kHex] =this.copayerIds[copayerIndex];
delete inKeyMap[kHex];
}
}

View file

@ -11,16 +11,16 @@ var buffertools = bitcore.buffertools;
var preconditions = require('preconditions').instance();
var VERSION = 1;
var CORE_FIELDS = ['builderObj','inputChainPaths', 'version'];
var CORE_FIELDS = ['builderObj', 'inputChainPaths', 'version'];
function TxProposal(opts) {
preconditions.checkArgument(opts);
preconditions.checkArgument(opts.inputChainPaths,'no inputChainPaths');
preconditions.checkArgument(opts.creator,'no creator');
preconditions.checkArgument(opts.createdTs,'no createdTs');
preconditions.checkArgument(opts.builder,'no builder');
preconditions.checkArgument(opts.inputChainPaths,'no inputChainPaths');
preconditions.checkArgument(opts.inputChainPaths, 'no inputChainPaths');
preconditions.checkArgument(opts.creator, 'no creator');
preconditions.checkArgument(opts.createdTs, 'no createdTs');
preconditions.checkArgument(opts.builder, 'no builder');
preconditions.checkArgument(opts.inputChainPaths, 'no inputChainPaths');
this.inputChainPaths = opts.inputChainPaths;
this.version = opts.version;
@ -38,11 +38,63 @@ function TxProposal(opts) {
this.sentTxid = opts.sentTxid || null;
this.comment = opts.comment || null;
this.readonly = opts.readonly || null;
this.sync();
this._sync();
}
TxProposal.prototype._check = function() {
if (this.builder.signhash && this.builder.signhash !== Transaction.SIGHASH_ALL) {
throw new Error('Invalid tx proposal');
}
var tx = this.builder.build();
if (!tx.ins.length)
throw new Error('Invalid tx proposal: no ins');
for (var i in tx.ins) {
var scriptSig = tx.ins[i].s;
if (!scriptSig || !scriptSig.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)
throw new Error('Invalid tx proposal: bad signatures');
}
};
TxProposal.prototype._updateSignedBy = function() {
this._inputSignatures = [];
var tx = this.builder.build();
for (var i in tx.ins) {
var scriptSig = new Script(tx.ins[i].s);
var signatureCount = scriptSig.countSignatures();
var info = TxProposal._infoFromRedeemScript(scriptSig);
var txSigHash = tx.hashForSignature(info.script, parseInt(i), Transaction.SIGHASH_ALL);
var signatureIndexes = TxProposal._verifySignatures(info.keys, scriptSig, txSigHash);
if (signatureIndexes.length !== signatureCount)
throw new Error('Invalid signature');
this._inputSignatures[i] = signatureIndexes.map(function(i) {
var r = info.keys[i].toString('hex');
return r;
});
};
};
TxProposal.prototype._sync = function() {
this._check();
this._updateSignedBy();
return this;
}
TxProposal.prototype.getId = function() {
preconditions.checkState(this.builder);
return this.builder.build().getNormalizedHash().toString('hex');
};
@ -54,23 +106,15 @@ TxProposal.prototype.toObj = function() {
};
TxProposal.prototype.toObjForNetwork = function() {
var o = this.toObj;
var newOutput = {};
CORE_FIELDS.forEach(function(k){
newOutput[k] = o[k];
TxProposal.trim = function() {
var o = this.toObj();
var ret = {};
CORE_FIELDS.forEach(function(k) {
ret[k] = o[k];
});
return newOutput;
return ret;
};
TxProposal.prototype.sync = function() {
this._check();
this._updateSignedBy();
return this;
}
// fromObj => from a trusted source
TxProposal.fromObj = function(o, forceOpts) {
preconditions.checkArgument(o.builderObj);
@ -93,17 +137,6 @@ TxProposal.fromObj = function(o, forceOpts) {
return new TxProposal(o);
};
TxProposal.fromObjUntrusted = function(o, forceOpts, senderId) {
var newInput = {};
CORE_FIELDS.forEach(function(k){
newInput[k] = o[k];
});
if (newInput.version !== VERSION)
throw new Error('Peer using different version');
return TxProposal.fromObj(newInput, forceOpts, senderId);
};
TxProposal._formatKeys = function(keys) {
@ -158,49 +191,6 @@ TxProposal._infoFromRedeemScript = function(s) {
};
};
TxProposal.prototype._updateSignedBy = function() {
this._inputSignatures = [];
var tx = this.builder.build();
for (var i in tx.ins) {
var scriptSig = new Script(tx.ins[i].s);
var signatureCount = scriptSig.countSignatures();
var info = TxProposal._infoFromRedeemScript(scriptSig);
var txSigHash = tx.hashForSignature(info.script, parseInt(i), Transaction.SIGHASH_ALL);
var signatureIndexes = TxProposal._verifySignatures(info.keys, scriptSig, txSigHash);
if (signatureIndexes.length !== signatureCount)
throw new Error('Invalid signature');
this._inputSignatures[i] = signatureIndexes.map(function(i) {
var r = info.keys[i].toString('hex');
return r;
});
};
};
TxProposal.prototype._check = function() {
if (this.builder.signhash && this.builder.signhash !== Transaction.SIGHASH_ALL) {
throw new Error('Invalid tx proposal');
}
var tx = this.builder.build();
if (!tx.ins.length)
throw new Error('Invalid tx proposal: no ins');
for(var i in tx.ins){
var scriptSig = tx.ins[i].s;
if (!scriptSig || !scriptSig.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)
throw new Error('Invalid tx proposal: bad signatures');
}
};
TxProposal.prototype.mergeBuilder = function(incoming) {
var b0 = this.builder;
var b1 = incoming.builder;
@ -213,12 +203,12 @@ TxProposal.prototype.mergeBuilder = function(incoming) {
TxProposal.prototype.setSeen = function(copayerId) {
if (!this.seenBy[copayerId])
if (!this.seenBy[copayerId])
this.seenBy[copayerId] = Date.now();
};
TxProposal.prototype.setRejected = function(copayerId) {
if (!this.rejectedBy[copayerId] && !this.signedBy)
if (!this.rejectedBy[copayerId] && !this.signedBy)
this.rejectedBy[copayerId] = Date.now();
};
@ -227,55 +217,78 @@ TxProposal.prototype.setSent = function(sentTxid) {
this.sentTs = Date.now();
};
/* OTDO
events.push({
type: 'seen',
cId: k,
txId: ntxid
});
events.push({
type: 'signed',
cId: k,
txId: ntxid
});
events.push({
type: 'rejected',
cId: k,
txId: ntxid
});
ret.events = this.mergeMetadata(incoming);
*/
TxProposal.prototype._allSignatures = function() {
var ret = {};
for(var i in this._inputSignatures)
for (var i in this._inputSignatures)
for (var j in this._inputSignatures[i])
ret[this._inputSignatures[i][j]] = true;
return ret;
};
TxProposal.prototype.setCopayers = function(senderId, keyMap, readOnlyPeers) {
var newCopayers = {},
oldCopayers = {}, newSignedBy = {}, readOnlyPeers = {}, isNew = 1;
for(var k in this.signedBy) {
oldCopayers[k] = 1;
isNew = 0;
};
if (isNew == 0 && (!this.creator || !this.createdTs))
throw new Error('Existing TX has no creator');
if (isNew == 0 && (!this.signedBy[this.creator]))
throw new Error('Existing TX is not signed by creator');
var iSig = this._inputSignatures[0];
for(var i in iSig){
var copayerId = keyMap[iSig[i]];
if (!copayerId)
throw new Error('Found unknown signature')
if (oldCopayers[copayerId]) {
//Already have it. Do nothing
} else {
newCopayers[copayerId] = Date.now();
delete oldCopayers[i];
}
}
if (!newCopayers[senderId] && !readOnlyPeers[senderId])
throw new Error('TX must have a (new) senders signature')
if (isNew && Object.keys(newCopayers).length>1)
throw new Error('New TX must have only 1 signature');
// Handler creator / createdTs.
// from senderId, and must be signed by senderId
if (isNew) {
this.creator = Object.keys(newCopayers)[0];
this.createdTs = Date.now();
}
//Ended. Update this.
for(var i in newCopayers) {
this.signedBy[i] = newCopayers[i];
}
// signedBy has preference over rejectedBy
for(var i in this.signedBy) {
delete this.rejectedBy[i];
}
return Object.keys(newCopayers);
};
// merge will not merge any metadata.
TxProposal.prototype.merge = function(incoming) {
var ret = {};
var newSignatures = [];
incoming.sync();
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;
var hasChanged = this.mergeBuilder(incoming);
this._sync();
return hasChanged;
};
//This should be on bitcore / Transaction

View file

@ -56,21 +56,30 @@ TxProposals.prototype.toObj = function() {
};
TxProposals.prototype.merge = function(inTxp, allowedPubKeys) {
var myTxps = this.txps;
TxProposals.prototype.merge = function(inObj, senderId, copayersForPubkeys, builderOpts) {
var safeObj = inObj.trimUntrustedObj();
var incomingTx = TxProposal.fromObj(safeObj, builderOpts);
incomingTx._sync();
var myTxps = this.txps;
var ntxid = inTxp.getId();
var ret = {};
var ret = {
ntxid: ntxid
};
if (myTxps[ntxid]) {
var v0 = myTxps[ntxid];
var v1 = inTxp;
ret = v0.merge(v1, allowedPubKeys);
}
else {
this.txps[ntxid] = inTxp;
// Merge an existing txProposal
ret.hasChanged = myTxps[ntxid].merge(inTxp, allowedPubKeys);
} else {
// Create a new one
ret.new = 1;
this.txps[ntxid] = inTxp;
}
ret.txp = this.txps[ntxid];
return ret;
};

View file

@ -59,7 +59,7 @@ function Wallet(opts) {
}
Wallet.builderOpts = {
Wallet.builderOpts = {
lockTime: null,
signhash: bitcore.Transaction.SIGNHASH_ALL,
fee: null,
@ -132,72 +132,119 @@ Wallet.prototype._handlePublicKeyRing = function(senderId, data, isInbound) {
Wallet.prototype._processProposalEvents = function(mergeInfo) {
var ev = [];
if (mergeInfo.new) {
ev = {
type: 'new',
cid: senderId
if (mergeInfo) {
if (mergeInfo.new) {
ev = {
type: 'new',
cid: senderId
}
} else {
for (var i in mergeInfo.newCopayers) {
var copayerId = mergeInfo.newCopayers[i];
ev.push({
type: 'signed',
cid: copayerId
});
}
}
} else {
for (var i in mergeInfo.newCopayers) {
var copayerId = mergeInfo.newCopayers[i];
ev.push({
type: 'signed',
cid: copayerId
});
}
ev = {
type: 'corrupt',
cId: senderId,
error: e,
};
}
if (ev)
this.emit('txProposalEvent', ev);
};
/* OTDO
events.push({
type: 'signed',
cId: k,
txId: ntxid
});
*/
Wallet.prototype._getKeyMap = function(tpx, senderId) {
this.publicKeyRing.copayersForPubkeys(txp._inputSignatures[0], txp.paths);
var keyMapStr = JSON.stringify(keyMap);
// All inputs must be signed with the same copayers
for (var i in m.txp._inputSignatures) {
if (!i) continue;
var inputKeyMapStr = JSON.stringify(
this.publicKeyRing.copayersForPubkeys(txp._inputSignatures[i], txp.paths));
if (inputKeyMapStr !== keyMapStr)
throw new Error('found inputs with different signatures in Tx from:' + senderId);
}
if (ev)
this.emit('txProposalEvent',ev);
};
Wallet.prototype._handleTxProposal = function(senderId, data) {
this.log('RECV TXPROPOSAL: ', data);
var mergeInfo, ntxid;
var m;
try {
mergeInfo = this.txProposals.mergeFromObj(data.txProposal, senderId, Wallet.builderOpts);
mergeInfo.newCopayers=[];
for (var i in mergeInfo.newSignatures) {
var k = mergeInfo.newSignatures[i];
mergeInfo.newCopayers.push(this.getCopayerIdFromPubKey(k));
};
ntxid = mergeInfo.inTxp.getId();
m = this.txProposals.mergeObj(senderId, data.txProposal, Wallet.builderOpts);
var keyMap = this._getKeyMap(m.tpx,senderId);
ret.newCopayers = m.txp.setCopayers(senderId, keyMap);
} catch (e) {
var corruptEvent = {
type: 'corrupt',
cId: senderId,
error: e,
};
this.emit('txProposalEvent', corruptEvent);
return;
this.log('Corrupt TX proposal received', senderId, e); //TODO
}
this.sendSeen(ntxid);
if (mergeInfo.hasChanged)
this.sendTxProposal(ntxid);
if (m) {
this.emit('txProposalsUpdated');
this.store();
this.emit('txProposalsUpdated');
this.store();
this._processProposalEvents(senderId, mergeInfo);
this.sendSeen(m.ntxid);
if (m.hasChanged)
this.sendTxProposal(m.ntxid);
}
this._processProposalEvents(senderId, m);
};
Wallet.prototype._handleReject = function(senderId, data, isInbound) {
this.log('RECV REJECT:', data);
// TODO check that has not signed.
//
this.txProposals.txps[data.ntxid].setRejected(senderId);
this.emit('txProposalsUpdated');
var txp = this.txProposals.txps[data.ntxid];
if (!txp)
throw new Error('Received Reject for an unkwown TX from:' + senderId);
if (txp.signedBy[senderId])
throw new Error('Received Reject for an already signed TX from:' + senderId);
txp.setRejected(senderId);
this.store();
this.emit('txProposalsUpdated');
this.emit('txProposalEvent', {
type: 'rejected',
cId: senderId,
txId: data.ntxid,
});
};
Wallet.prototype._handleSeen = function(senderId, data, isInbound) {
this.log('RECV SEEN:', data);
this.txProposals.txps[data.ntxid].setSeen(senderId);
this.emit('txProposalsUpdated');
this.store();
this.emit('txProposalsUpdated');
this.emit('txProposalEvent', {
type: 'seen',
cId: senderId,
txId: data.ntxid,
});
};
@ -245,8 +292,10 @@ Wallet.prototype._handleData = function(senderId, data, isInbound) {
break;
case 'reject':
this._handleReject(senderId, data, isInbound);
break;
case 'seen':
this._handleReject(senderId, data, isInbound);
this._handleSeen(senderId, data, isInbound);
break;
case 'txProposal':
this._handleTxProposal(senderId, data, isInbound);
break;
@ -796,7 +845,7 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
};
}
for (var k in Wallet.builderOpts){
for (var k in Wallet.builderOpts) {
opts[k] = Wallet.builderOpts[k];
}
@ -821,8 +870,8 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
var tx = b.build();
if (!tx.countInputSignatures(0))
throw new Error ('Could not sign generated tx');
if (!tx.countInputSignatures(0))
throw new Error('Could not sign generated tx');
var me = {};
me[myId] = now;

View file

@ -161,28 +161,30 @@ describe('TxProposal', function() {
}).should.throw('script');
});
it('#_verifyScriptSig, no signatures', function() {
var ret = TxProposal._verifySignatures( keyBuf, validScriptSig, new Buffer(32));
var ret = TxProposal._verifySignatures(keyBuf, validScriptSig, new Buffer(32));
ret.length.should.equal(0);
});
it('#_verifyScriptSig, two signatures', function() {
// Data taken from bitcore's TransactionBuilder test
var txp = dummyProposal;
var tx = dummyProposal.builder.build();
var ret = TxProposal._verifySignatures(pubkeys,validScriptSig, tx.hashForSignature());
var ret = TxProposal._verifySignatures(pubkeys, validScriptSig, tx.hashForSignature());
ret.should.deep.equal([0, 3]);
});
it('#_infoFromRedeemScript', function() {
var info = TxProposal._infoFromRedeemScript(validScriptSig);
var keys = info.keys;
keys.length.should.equal(5);
for(var i in keys){
for (var i in keys) {
keys[i].toString('hex').should.equal(pubkeys[i].toString('hex'));
}
Buffer.isBuffer(info.script.getBuffer()).should.equal(true);
});
it('#_updateSignedBy', function() {
var txp = dummyProposal;
txp._inputSignatures.should.deep.equal([[ '03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3' ]]);
txp._inputSignatures.should.deep.equal([
['03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3']
]);
});
describe('#_check', function() {
var txp = dummyProposal;
@ -193,28 +195,38 @@ describe('TxProposal', function() {
});
it('FAIL ins', function() {
txp.builder.tx.ins = [];
(function() { txp._check();} ).should.throw('no ins');
(function() {
txp._check();
}).should.throw('no ins');
txp.builder.tx.ins = backup;
});
it('FAIL signhash SINGLE', function() {
sinon.stub(txp.builder.tx,'getHashType').returns(Transaction.SIGHASH_SINGLE);
(function() { txp._check();} ).should.throw('signatures');
sinon.stub(txp.builder.tx, 'getHashType').returns(Transaction.SIGHASH_SINGLE);
(function() {
txp._check();
}).should.throw('signatures');
txp.builder.tx.getHashType.restore();
});
it('FAIL signhash NONE', function() {
sinon.stub(txp.builder.tx,'getHashType').returns(Transaction.SIGHASH_NONE);
(function() { txp._check();} ).should.throw('signatures');
sinon.stub(txp.builder.tx, 'getHashType').returns(Transaction.SIGHASH_NONE);
(function() {
txp._check();
}).should.throw('signatures');
txp.builder.tx.getHashType.restore();
});
it('FAIL signhash ANYONECANPAY', function() {
sinon.stub(txp.builder.tx,'getHashType').returns(Transaction.SIGHASH_ANYONECANPAY);
(function() { txp._check();} ).should.throw('signatures');
sinon.stub(txp.builder.tx, 'getHashType').returns(Transaction.SIGHASH_ANYONECANPAY);
(function() {
txp._check();
}).should.throw('signatures');
txp.builder.tx.getHashType.restore();
});
it('FAIL no signatures', function() {
var backup = txp.builder.tx.ins[0].s;
txp.builder.tx.ins[0].s = undefined;
(function() { txp._check();} ).should.throw('no signatures');
(function() {
txp._check();
}).should.throw('no signatures');
txp.builder.tx.ins[0].s = backup;
});
});
@ -222,39 +234,179 @@ describe('TxProposal', 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);
var hasChanged = txp.merge(txp);
hasChanged.should.equal(false);
});
it('with less signatures', function() {
var backup = txp.builder.vanilla.scriptSig[0];
txp.builder.merge = function() {
// 3 signatures.
this.vanilla.scriptSig=['0048304502207d8e832bd576c93300e53ab6cbd68641961bec60690c358fd42d8e42b7d7d687022100a1daa89923efdb4c9b615d065058d9e1644f67000694a7d0806759afa7bef19b014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae'];
this.tx.ins[0].s=new Buffer(this.vanilla.scriptSig[0],'hex');
this.vanilla.scriptSig = ['0048304502207d8e832bd576c93300e53ab6cbd68641961bec60690c358fd42d8e42b7d7d687022100a1daa89923efdb4c9b615d065058d9e1644f67000694a7d0806759afa7bef19b014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae'];
this.tx.ins[0].s = new Buffer(this.vanilla.scriptSig[0], 'hex');
};
var ret = txp.merge(txp);
ret.hasChanged.should.equal(true);
ret.newSignatures.length.should.equal(0);
var hasChanged = txp.merge(txp);
hasChanged.should.equal(true);
txp.builder.vanilla.scriptSig = [backup];
txp.builder.tx.ins[0].s = new Buffer(backup,'hex');
txp.builder.tx.ins[0].s = new Buffer(backup, 'hex');
});
it('with more signatures', function() {
txp.builder.merge = function() {
// 3 signatures.
this.vanilla.scriptSig=['00483045022100f75bd3eb92d8c9be9a94d848bbd1985fc0eaf4c47fb470a0b222881802a1f03802204eb239ae3082779b1ec4f2e69baa0362494071e707e1696c14ad23c8f2e184e20148304502201981482db0f369ce943293b6fec06a0347918663c766a79d4cbd0457801768d1022100aedf8d7c51d55a9ddbdcc0067ed6b648b77ce9660447bbcf4e2c209698efa0a30148304502203f0ddad47757f8705cb40e7c706590d2e2028a7027ffdb26dd208fd6155e0d28022100ccd206f9b969ab7f88ee4c5c6cee48c800a62dda024c5a8de7eb8612b833a0c0014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae'];
this.tx.ins[0].s=new Buffer(this.vanilla.scriptSig[0],'hex');
this.vanilla.scriptSig = ['00483045022100f75bd3eb92d8c9be9a94d848bbd1985fc0eaf4c47fb470a0b222881802a1f03802204eb239ae3082779b1ec4f2e69baa0362494071e707e1696c14ad23c8f2e184e20148304502201981482db0f369ce943293b6fec06a0347918663c766a79d4cbd0457801768d1022100aedf8d7c51d55a9ddbdcc0067ed6b648b77ce9660447bbcf4e2c209698efa0a30148304502203f0ddad47757f8705cb40e7c706590d2e2028a7027ffdb26dd208fd6155e0d28022100ccd206f9b969ab7f88ee4c5c6cee48c800a62dda024c5a8de7eb8612b833a0c0014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae'];
this.tx.ins[0].s = new Buffer(this.vanilla.scriptSig[0], 'hex');
};
var ret = txp.merge(txp);
ret.hasChanged.should.equal(true);
ret.newSignatures.length.should.equal(1);
ret.newSignatures[0].should.equal('0392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed03');
var hasChanged = txp.merge(txp);
hasChanged.should.equal(true);
});
});
describe('#setCopayers', function() {
it("should fails if Tx has no creator", function() {
var txp = dummyProposal;
txp.signedBy = {
'hugo': 1
};
delete txp['creator'];
(function() {
txp.setCopayers('juan', {
pk1: 'pepe'
})
}).should.throw('no creator');
});
it("should fails if Tx is not signed by creator", function() {
var txp = dummyProposal;
txp.creator = 'creator';
txp.signedBy = {
'hugo': 1
};
txp._inputSignatures = [
['pkX']
];
(function() {
txp.setCopayers('juan', {
pk1: 'pepe'
})
}).should.throw('creator');
});
it("should fails if Tx has unmapped signatures", function() {
var txp = dummyProposal;
txp.creator = 'creator';
txp.signedBy = {
creator: 1
};
txp._inputSignatures = [
['pk0', 'pkX']
];
(function() {
txp.setCopayers('juan', {
pk1: 'pepe'
})
}).should.throw('unknown sig');
});
it("should be signed by sender", function() {
var txp = dummyProposal;
var ts = Date.now();
txp._inputSignatures = [
['pk1', 'pk0']
];
txp.signedBy = {
'creator': Date.now()
};
(function() {
txp.setCopayers('juan', {
pk0: 'creator',
pk1: 'pepe',
pk2: 'john'
})
}).should.throw('senders sig');
});
it("should set signedBy (trivial case)", function() {
var txp = dummyProposal;
var ts = Date.now();
txp._inputSignatures = [
['pk1', 'pk0']
];
txp.signedBy = {
'creator': Date.now()
};
txp.setCopayers('pepe', {
pk0: 'creator',
pk1: 'pepe',
pk2: 'john'
})
Object.keys(txp.signedBy).length.should.equal(2);
txp.signedBy['pepe'].should.gte(ts);
txp.signedBy['creator'].should.gte(ts);
});
it("should assign creator", function() {
var txp = dummyProposal;
var ts = Date.now();
txp._inputSignatures = [
['pk0']
];
txp.signedBy = {};
delete txp['creator'];
delete txp['creatorTs'];
txp.setCopayers('creator', {
pk0: 'creator',
pk1: 'pepe',
pk2: 'john'
})
Object.keys(txp.signedBy).length.should.equal(1);
txp.creator.should.equal('creator');
txp.createdTs.should.gte(ts);
})
it("New tx should have only 1 signature", function() {
var txp = dummyProposal;
var ts = Date.now();
txp.signedBy = {};
delete txp['creator'];
delete txp['creatorTs'];
txp._inputSignatures = [
['pk0', 'pk1']
];
(function() {
txp.setCopayers(
'creator', {
pk0: 'creator',
pk1: 'pepe',
pk2: 'john'
}, {
'creator2': 1
}
);
}).should.throw('only 1');
})
it("if signed, should not change ts", function() {
var txp = dummyProposal;
var ts = Date.now();
txp._inputSignatures = [
['pk0', 'pk1']
];
txp.creator = 'creator';
txp.signedBy = {
'creator': 1
};
txp.setCopayers('pepe', {
pk0: 'creator',
pk1: 'pepe',
pk2: 'john'
})
Object.keys(txp.signedBy).length.should.equal(2);
txp.creator.should.equal('creator');
txp.signedBy['creator'].should.equal(1);
txp.signedBy['pepe'].should.gte(ts);
})
});
});
});