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

@ -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