Merge pull request #281 from matiu/feature/signature-sorting

Feature/signature sorting
This commit is contained in:
Ryan X. Charles 2014-04-22 23:17:41 -03:00
commit 11b54b4b3d
6 changed files with 453 additions and 219 deletions

View File

@ -119,34 +119,72 @@ Script.prototype.isMultiSig = function() {
this.chunks[this.chunks.length - 1] == OP_CHECKMULTISIG); this.chunks[this.chunks.length - 1] == OP_CHECKMULTISIG);
}; };
Script.prototype.finishedMultiSig = function() { Script.prototype.isP2shScriptSig = function() {
var nsigs = 0; if( !isSmallIntOp(this.chunks[0]) || this.chunks[0] !==0 )
for (var i = 0; i < this.chunks.length - 1; i++)
if (this.chunks[i] !== 0)
nsigs++;
var serializedScript = this.chunks[this.chunks.length - 1];
var script = new Script(serializedScript);
var nreq = script.chunks[0] - 80; //see OP_2-OP_16
if (nsigs == nreq)
return true;
else
return false; return false;
var redeemScript = new Script(this.chunks[this.chunks.length-1]);
var type=redeemScript.classify();
return type !== TX_UNKNOWN;
}; };
Script.prototype.removePlaceHolders = function() { Script.prototype.isMultiSigScriptSig = function() {
var chunks = []; if( !isSmallIntOp(this.chunks[0]) || this.chunks[0] !==0 )
for (var i in this.chunks) { return false;
if (this.chunks.hasOwnProperty(i)) { return !this.isP2shScriptSig();
var chunk = this.chunks[i]; };
if (chunk != 0)
chunks.push(chunk); Script.prototype.countSignatures = function() {
var ret = 0;
var l =this.chunks.length;
// Multisig?
if (this.isMultiSigScriptSig()){
ret = l - 1;
}
else if (this.isP2shScriptSig()) {
ret = l - 2;
}
// p2pubkey or p2pubkeyhash
else {
ret = buffertools.compare(this.getBuffer(), util.EMPTY_BUFFER)===0?0:1;
}
return ret;
};
Script.prototype.countMissingSignatures = function() {
if (this.isMultiSig()) {
log.debug("Can not count missing signatures on normal Multisig script");
return null;
}
var ret = 0;
var l =this.chunks.length;
// P2SH?
if (isSmallIntOp(this.chunks[0]) && this.chunks[0] ===0) {
var redeemScript = new Script(this.chunks[l-1]);
if (!isSmallIntOp(redeemScript.chunks[0])) {
log.debug("Unrecognized script type");
}
else {
var nreq = redeemScript.chunks[0] - 80; //see OP_2-OP_16
ret = nreq - (l - 2); // 2-> marked 0 + redeemScript
} }
} }
this.chunks = chunks; // p2pubkey or p2pubkeyhash
this.updateBuffer(); else {
return this; if (buffertools.compare(this.getBuffer(), util.EMPTY_BUFFER) === 0) {
ret = 1;
}
}
return ret;
};
Script.prototype.finishedMultiSig = function() {
var missing = this.countMissingSignatures();
if (missing === null) return null;
return missing === 0;
}; };
Script.prototype.prependOp0 = function() { Script.prototype.prependOp0 = function() {
@ -517,25 +555,6 @@ Script.prototype.toHumanReadable = function() {
return s; return s;
}; };
Script.prototype.countMissingSignatures = function() {
var ret = 0;
if (!Buffer.isBuffer(this.chunks[0]) && this.chunks[0] ===0) {
// Multisig, skip first 0x0
for (var i = 1; i < this.chunks.length; i++) {
if (this.chunks[i]===0
|| buffertools.compare(this.chunks[i], util.EMPTY_BUFFER) === 0){
ret++;
}
}
}
else {
if (buffertools.compare(this.getBuffer(), util.EMPTY_BUFFER) === 0) {
ret = 1;
}
}
return ret;
};
Script.stringToBuffer = function(s) { Script.stringToBuffer = function(s) {
var buf = new Put(); var buf = new Put();
var split = s.split(' '); var split = s.split(' ');

View File

@ -593,18 +593,27 @@ Transaction.prototype.getSize = function () {
return this.size; return this.size;
}; };
Transaction.prototype.countInputSignatures = function(index) {
var ret = 0;
var script = new Script(this.ins[index].s);
return script.countSignatures();
};
// Works on p2pubkey, p2pubkeyhash & p2sh (no normal multisig)
Transaction.prototype.countInputMissingSignatures = function(index) { Transaction.prototype.countInputMissingSignatures = function(index) {
var ret = 0; var ret = 0;
var script = new Script(this.ins[index].s); var script = new Script(this.ins[index].s);
return script.countMissingSignatures(); return script.countMissingSignatures();
}; };
// Works on p2pubkey, p2pubkeyhash & p2sh (no normal multisig)
Transaction.prototype.isInputComplete = function(index) { Transaction.prototype.isInputComplete = function(index) {
return this.countInputMissingSignatures(index)===0; var m = this.countInputMissingSignatures(index);
if (m===null) return null;
return m === 0;
}; };
// Works on p2pubkey, p2pubkeyhash & p2sh (no normal multisig)
Transaction.prototype.isComplete = function() { Transaction.prototype.isComplete = function() {
var ret = true; var ret = true;
var l = this.ins.length; var l = this.ins.length;
@ -615,7 +624,6 @@ Transaction.prototype.isComplete = function() {
break; break;
} }
} }
return ret; return ret;
}; };

View File

@ -88,6 +88,8 @@ var buffertools = imports.buffertools || require('buffertools');
var networks = imports.networks || require('../networks'); var networks = imports.networks || require('../networks');
var WalletKey = imports.WalletKey || require('./WalletKey'); var WalletKey = imports.WalletKey || require('./WalletKey');
var PrivateKey = imports.PrivateKey || require('./PrivateKey'); var PrivateKey = imports.PrivateKey || require('./PrivateKey');
var Key = imports.Key || require('./Key');
var log = imports.log || require('../util/log');
var Transaction = imports.Transaction || require('./Transaction'); var Transaction = imports.Transaction || require('./Transaction');
var FEE_PER_1000B_SAT = parseInt(0.0001 * util.COIN); var FEE_PER_1000B_SAT = parseInt(0.0001 * util.COIN);
@ -481,7 +483,7 @@ TransactionBuilder.prototype._signPubKey = function(walletKeyMap, input, txSigHa
var scriptSig = new Script(); var scriptSig = new Script();
scriptSig.chunks.push(sig); scriptSig.chunks.push(sig);
scriptSig.updateBuffer(); scriptSig.updateBuffer();
return {isFullySigned: true, signaturesAdded: 1, script: scriptSig.getBuffer()}; return {inputFullySigned: true, signaturesAdded: 1, script: scriptSig.getBuffer()};
}; };
TransactionBuilder.prototype._signPubKeyHash = function(walletKeyMap, input, txSigHash) { TransactionBuilder.prototype._signPubKeyHash = function(walletKeyMap, input, txSigHash) {
@ -500,73 +502,89 @@ TransactionBuilder.prototype._signPubKeyHash = function(walletKeyMap, input, txS
scriptSig.chunks.push(sig); scriptSig.chunks.push(sig);
scriptSig.chunks.push(wk.privKey.public); scriptSig.chunks.push(wk.privKey.public);
scriptSig.updateBuffer(); scriptSig.updateBuffer();
return {isFullySigned: true, signaturesAdded: 1, script: scriptSig.getBuffer()}; return {inputFullySigned: true, signaturesAdded: 1, script: scriptSig.getBuffer()};
}; };
// FOR TESTING // FOR TESTING
var _dumpChunks = function (scriptSig, label) { // var _dumpChunks = function (scriptSig, label) {
console.log('## DUMP: ' + label + ' ##'); // console.log('## DUMP: ' + label + ' ##');
for(var i=0; i<scriptSig.chunks.length; i++) { // for(var i=0; i<scriptSig.chunks.length; i++) {
console.log('\tCHUNK ', i, scriptSig.chunks[i]); // console.log('\tCHUNK ', i, Buffer.isBuffer(scriptSig.chunks[i])
} // ?scriptSig.chunks[i].toString('hex'):scriptSig.chunks[i] );
}; // }
// };
TransactionBuilder.prototype._initMultiSig = function(scriptSig, nreq) {
var wasUpdated = false;
if (scriptSig.chunks.length < nreq + 1) {
wasUpdated = true;
scriptSig.writeN(0);
while (scriptSig.chunks.length <= nreq)
scriptSig.chunks.push(util.EMPTY_BUFFER);
}
return wasUpdated;
};
TransactionBuilder.prototype._isSignedWithKey = function(wk, scriptSig, txSigHash, nreq) { TransactionBuilder.prototype._chunkSignedWithKey = function(scriptSig, txSigHash, publicKey) {
var ret=false; var ret;
// _dumpChunks(scriptSig); var k = new Key();
for(var i=1; i<=nreq; i++) { k.public =publicKey;
for(var i=1; i<= scriptSig.countSignatures(); i++) {
var chunk = scriptSig.chunks[i]; var chunk = scriptSig.chunks[i];
if (chunk ===0 || chunk.length === 0) continue;
var sigRaw = new Buffer(chunk.slice(0,chunk.length-1)); var sigRaw = new Buffer(chunk.slice(0,chunk.length-1));
if(wk.privKey.verifySignatureSync(txSigHash, sigRaw) === true) { if (k.verifySignatureSync(txSigHash, sigRaw) ) {
ret=true; ret=chunk;
} }
} }
return ret; return ret;
}; };
TransactionBuilder.prototype._getSignatureOrder = function(sigPrio, sigRaw, txSigHash, pubkeys) {
var l=pubkeys.length;
for(var j=0; j<l; j++) {
var k = new Key();
k.public = new Buffer(pubkeys[j],'hex');
if (k.verifySignatureSync(txSigHash, sigRaw))
break;
}
return j;
};
TransactionBuilder.prototype._getNewSignatureOrder = function(sigPrio, scriptSig, txSigHash, pubkeys) {
var iPrio;
for(var i=1; i<= scriptSig.countSignatures(); i++) {
var chunk = scriptSig.chunks[i];
var sigRaw = new Buffer(chunk.slice(0,chunk.length-1));
iPrio = this._getSignatureOrder(sigPrio, sigRaw, txSigHash, pubkeys);
if (sigPrio <= iPrio) break;
}
return (sigPrio === iPrio? -1: i-1);
};
TransactionBuilder.prototype._chunkIsEmpty = function(chunk) { TransactionBuilder.prototype._chunkIsEmpty = function(chunk) {
return chunk === 0 || // when serializing and back, EMPTY_BUFFER becomes 0 return chunk === 0 || // when serializing and back, EMPTY_BUFFER becomes 0
buffertools.compare(chunk, util.EMPTY_BUFFER) === 0; buffertools.compare(chunk, util.EMPTY_BUFFER) === 0;
}; };
TransactionBuilder.prototype._initMultiSig = function(script) {
TransactionBuilder.prototype._updateMultiSig = function(wk, scriptSig, txSigHash, nreq) { var wasUpdated = false;
var wasUpdated = this._initMultiSig(scriptSig, nreq); if (script.chunks[0] !== 0) {
script.prependOp0();
if (this._isSignedWithKey(wk,scriptSig, txSigHash, nreq)) wasUpdated = true;
return null;
// Find an empty slot and sign
for(var i=1; i<=nreq; i++) {
var chunk = scriptSig.chunks[i];
if (!this._chunkIsEmpty(chunk))
continue;
// Add signature
var sigRaw = TransactionBuilder._signHashAndVerify(wk, txSigHash);
var sigType = new Buffer(1);
sigType[0] = this.signhash;
var sig = Buffer.concat([sigRaw, sigType]);
scriptSig.chunks[i] = sig;
scriptSig.updateBuffer();
wasUpdated=true;
break;
} }
// _dumpChunks(scriptSig); // TODO return wasUpdated;
};
TransactionBuilder.prototype._updateMultiSig = function(sigPrio, wk, scriptSig, txSigHash, pubkeys) {
var wasUpdated = this._initMultiSig(scriptSig);
if (this._chunkSignedWithKey(scriptSig, txSigHash, wk.privKey.public))
return null;
// Create signature
var sigRaw = TransactionBuilder._signHashAndVerify(wk, txSigHash);
var sigType = new Buffer(1);
sigType[0] = this.signhash;
var sig = Buffer.concat([sigRaw, sigType]);
// Add signature
var order = this._getNewSignatureOrder(sigPrio,scriptSig,txSigHash,pubkeys);
scriptSig.chunks.splice(order+1,0,sig);
scriptSig.updateBuffer();
wasUpdated=true;
return wasUpdated ? scriptSig : null; return wasUpdated ? scriptSig : null;
}; };
@ -580,22 +598,26 @@ TransactionBuilder.prototype._signMultiSig = function(walletKeyMap, input, txSig
var scriptSig = new Script (originalScriptBuf); var scriptSig = new Script (originalScriptBuf);
var signaturesAdded = 0; var signaturesAdded = 0;
for(var j=0; j<l && scriptSig.countMissingSignatures(); j++) { for(var j=0; j<l && scriptSig.countSignatures() < nreq ; j++) {
//console.log('[TransactionBuilder.js] pubkey [j]',j, pubkeys[j].toString('hex')); //TODO
var wk = this._findWalletKey(walletKeyMap, {pubKeyBuf: pubkeys[j]}); var wk = this._findWalletKey(walletKeyMap, {pubKeyBuf: pubkeys[j]});
if (!wk) continue; if (!wk) continue;
var newScriptSig = this._updateMultiSig(wk, scriptSig, txSigHash, nreq); var newScriptSig = this._updateMultiSig(j, wk, scriptSig, txSigHash, pubkeys);
if (newScriptSig) { if (newScriptSig) {
scriptSig = newScriptSig; scriptSig = newScriptSig;
signaturesAdded++; signaturesAdded++;
} }
} }
if (scriptSig.countSignatures() === nreq) {
}
return { var ret = {
isFullySigned: scriptSig.countMissingSignatures() === 0, inputFullySigned: scriptSig.countSignatures() === nreq,
signaturesAdded: signaturesAdded, signaturesAdded: signaturesAdded,
script: scriptSig.getBuffer(), script: scriptSig.getBuffer(),
}; };
return ret;
}; };
var fnToSign = {}; var fnToSign = {};
@ -641,13 +663,11 @@ TransactionBuilder.prototype._getInputForP2sh = function(script, index) {
pubKeyHash: pubKeyHash, pubKeyHash: pubKeyHash,
scriptPubKey: script, scriptPubKey: script,
scriptType: scriptType, scriptType: scriptType,
isP2sh: true,
}; };
}; };
TransactionBuilder.prototype._p2shInput = function(input) {
TransactionBuilder.prototype._signScriptHash = function(walletKeyMap, input, txSigHash) {
var originalScriptBuf = this.tx.ins[input.i].s;
if (!this.hashToScriptMap) if (!this.hashToScriptMap)
throw new Error('hashToScriptMap not set'); throw new Error('hashToScriptMap not set');
@ -661,12 +681,21 @@ TransactionBuilder.prototype._signScriptHash = function(walletKeyMap, input, txS
if (!fnToSign[scriptType] || scriptType === Script.TX_SCRIPTHASH) if (!fnToSign[scriptType] || scriptType === Script.TX_SCRIPTHASH)
throw new Error('dont know how to sign p2sh script type:'+ script.getRawOutType()); throw new Error('dont know how to sign p2sh script type:'+ script.getRawOutType());
var newInput = this._getInputForP2sh(script, input.i); return {
var newTxSigHash = this.tx.hashForSignature( script, newInput.i, this.signhash); input: this._getInputForP2sh(script, input.i),
var ret = fnToSign[scriptType].call(this, walletKeyMap, newInput, newTxSigHash); txSigHash: this.tx.hashForSignature( script, input.i, this.signhash),
scriptType: script.classify(),
scriptBuf: scriptBuf,
};
};
TransactionBuilder.prototype._signScriptHash = function(walletKeyMap, input, txSigHash) {
var p2sh = this._p2shInput(input);
var ret = fnToSign[p2sh.scriptType].call(this, walletKeyMap, p2sh.input, p2sh.txSigHash);
if (ret && ret.script && ret.signaturesAdded) { if (ret && ret.script && ret.signaturesAdded) {
ret.script = this._addScript(ret.script, scriptBuf); ret.script = this._addScript(ret.script, p2sh.scriptBuf);
} }
return ret; return ret;
}; };
@ -692,7 +721,7 @@ TransactionBuilder.prototype.sign = function(keys) {
var ret = fnToSign[input.scriptType].call(this, walletKeyMap, input, txSigHash); var ret = fnToSign[input.scriptType].call(this, walletKeyMap, input, txSigHash);
if (ret && ret.script) { if (ret && ret.script) {
tx.ins[i].s = ret.script; tx.ins[i].s = ret.script;
if (ret.isFullySigned) this.inputsSigned++; if (ret.inputFullySigned) this.inputsSigned++;
if (ret.signaturesAdded) this.signaturesAdded +=ret.signaturesAdded; if (ret.signaturesAdded) this.signaturesAdded +=ret.signaturesAdded;
} }
} }
@ -826,69 +855,76 @@ TransactionBuilder.prototype._checkMergeability = function(b) {
}; };
TransactionBuilder.prototype._mergeInputSigP2sh = function(input,s0,s1) {
var p2sh = this._p2shInput(input);
var redeemScript = new Script(p2sh.scriptBuf);
var pubkeys = redeemScript.capture();
// this assumes that the same signature can not be v0 / v1 (which shouldnt be!) // Look for differences
TransactionBuilder.prototype._mergeInputSig = function(s0buf, s1buf, ignoreConflictingSignatures) { var s0keys = {};
if (buffertools.compare(s0buf,s1buf) === 0) { var l = pubkeys.length;
//console.log('BUFFERS .s MATCH'); //TODO for (var j=0; j<l; j++) {
return s0buf; if ( this._chunkSignedWithKey(s0, p2sh.txSigHash, pubkeys[j]))
s0keys[pubkeys[j].toString('hex')] = 1;
} }
// Is multisig?
var diff = [];
for (var j=0; j<l; j++) {
var chunk = this._chunkSignedWithKey(s1, p2sh.txSigHash, pubkeys[j]);
var pubHex = pubkeys[j].toString('hex');
if (chunk && !s0keys[pubHex]) {
diff.push({
prio: j,
chunk: chunk,
pubHex: pubHex,
});
}
}
// Add signatures
for(var j in diff) {
var newSig = diff[j];
var order = this._getNewSignatureOrder(newSig.prio,s0,p2sh.txSigHash,pubkeys);
s0.chunks.splice(order+1,0,newSig.chunk);
this.signaturesAdded++;
}
s0.updateBuffer();
return s0.getBuffer();
};
TransactionBuilder.prototype._mergeInputSig = function(index, s0buf, s1buf) {
if (buffertools.compare(s0buf,s1buf) === 0)
return s0buf;
var s0 = new Script(s0buf); var s0 = new Script(s0buf);
var s1 = new Script(s1buf); var s1 = new Script(s1buf);
var l0 = s0.chunks.length; var l0 = s0.chunks.length;
var l1 = s1.chunks.length; var l1 = s1.chunks.length;
var s0map = {}; var s0map = {};
if (l0 && l1 && l0 !== l1) if (l0 && l1 && ((l0<2 && l1>2) || (l1<2 && l0>2 )))
throw new Error('TX sig types mismatch in merge'); throw new Error('TX sig types mismatch in merge');
if (!l0 && !l1) return s0buf; if ((!l0 && !l1) || ( l0 && !l1) || (!l0 && l1))
if ( l0 && !l1) return s0buf; return s1buf;
if (!l0 && l1) return s1buf;
// Look for differences. // Get the pubkeys
for (var i=0; i<l0; i++) { var input = this.inputMap[index];
if (!this._chunkIsEmpty(s0.chunks[i])) var type = input.scriptPubKey.classify();
s0map[s0.chunks[i]] = 1;
};
var diff = []; //p2pubkey or p2pubkeyhash
for (var i=0; i<l1; i++) { if (type === Script.TX_PUBKEYHASH || type === Script.TX_PUBKEY) {
if ( !this._chunkIsEmpty(s1.chunks[i]) && !s0map[s1.chunks[i]]) { log.debug('Merging two signed inputs type:' +
diff.push(s1.chunks[i]); input.scriptPubKey.getRawOutType() + '. Signatures differs. Using the first version.');
} return s0buf;
};
if (!diff) {
console.log('[TransactionBuilder.js.857: NO DIFF FOUND, just ORDER DIFF]'); //TODO
return s0.getBuffer();
} }
else if (type!== Script.TX_SCRIPTHASH) {
var emptySlots = []; // No support for normal multisig or strange txs.
for (var i=1; i<l0; i++) { throw new Error('Script type:'+input.scriptPubKey.getRawOutType()+'not supported at #merge');
if (this._chunkIsEmpty(s0.chunks[i])) {
emptySlots.push(i);
}
} }
return this._mergeInputSigP2sh(input,s0, s1);
if (emptySlots.length<diff.length) {
if (!ignoreConflictingSignatures) {
throw new Error(
'no enough empty slots to merge Txs: Check ignoreConflictingSignatures option');
}
}
else {
for (var i=0; i<diff.length; i++) {
s0.chunks[emptySlots[i]] = diff[i];
this.signaturesAdded++;
}
s0.updateBuffer();
}
return s0.getBuffer();
}; };
TransactionBuilder.prototype._mergeTx = function(tx, ignoreConflictingSignatures) { TransactionBuilder.prototype._mergeTx = function(tx, ignoreConflictingSignatures) {
var v0 = this.tx; var v0 = this.tx;
var v1 = tx; var v1 = tx;
@ -905,33 +941,26 @@ TransactionBuilder.prototype._mergeTx = function(tx, ignoreConflictingSignatures
if (i0.q !== i1.q) if (i0.q !== i1.q)
throw new Error('TX sequence ins mismatch in merge. Input:',i); throw new Error('TX sequence ins mismatch in merge. Input:',i);
if (buffertools.compare(i0.o,i1.o) !== 0) if (buffertools.compare(i0.o,i1.o) !== 0)
throw new Error('TX .o in mismatch in merge. Input:',i); throw new Error('TX .o in mismatch in merge. Input:',i);
i0.s=this._mergeInputSig(i0.s,i1.s, ignoreConflictingSignatures); i0.s=this._mergeInputSig(i, i0.s,i1.s);
if (v0.isInputComplete(i)) this.inputsSigned++; if (v0.isInputComplete(i)) this.inputsSigned++;
} }
}; };
TransactionBuilder.prototype.merge = function(b) {
TransactionBuilder.prototype.merge = function(b, ignoreConflictingSignatures) {
this._checkMergeability(b); this._checkMergeability(b);
// Does this tX have any signature already? // Does this tX have any signature already?
if (this.tx || b.tx) { if (this.tx || b.tx) {
if (this.tx.getNormalizedHash().toString('hex') if (this.tx.getNormalizedHash().toString('hex')
!== b.tx.getNormalizedHash().toString('hex')) !== b.tx.getNormalizedHash().toString('hex'))
throw new Error('mismatch at TransactionBuilder NTXID'); throw new Error('mismatch at TransactionBuilder NTXID');
this._mergeTx(b.tx, ignoreConflictingSignatures); this._mergeTx(b.tx);
// TODO UPDATE: signaturesAdded, inputsSigned
} }
}; };
module.exports = require('soop')(TransactionBuilder); module.exports = require('soop')(TransactionBuilder);

View File

@ -66,10 +66,11 @@
"cMyBgowsyrJRufoKWob73rMQB1PBqDdwFt8z4TJ6APN2HkmX1Ttm", "cMyBgowsyrJRufoKWob73rMQB1PBqDdwFt8z4TJ6APN2HkmX1Ttm",
"cN9yZCom6hAZpHtCp8ovE1zFa7RqDf3Cr4W6AwH2tp59Jjh9JcXu" "cN9yZCom6hAZpHtCp8ovE1zFa7RqDf3Cr4W6AwH2tp59Jjh9JcXu"
], ],
"comment": "script pubkey can be obtained from: bitcoind createrawtransaction '[{\"txid\": \"2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1\",\"vout\":1}]' '{\"2NFW3ja1tdza4b1WTyG9fkz6cBtRf4qEFBh\":0.08}' and then decoding the generated transaction hex using bitcoind decoderawtransaction",
"unspentP2sh": [ "unspentP2sh": [
{ {
"address": "2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6", "address": "2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6",
"scriptPubKey": "a91432d272ce8a9b482b363408a0b1dd28123d59c63387", "scriptPubKey": "a914dc0623476aefb049066b09b0147a022e6eb8429187",
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1", "txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
"vout": 1, "vout": 1,
"amount": 1, "amount": 1,

View File

@ -44,8 +44,8 @@ describe('Script', function() {
script.finishedMultiSig().should.equal(true); script.finishedMultiSig().should.equal(true);
}); });
it('should report that this scripSig has not finished being signed', function() { it('should report that this scripSig has not finished being signed', function() {
var scriptHex = '483045022002da7fae9b98615115b7e9a4f1d9efd581463b670f91ec6404a14cb6fc9c4531022100ff449d72ba4e72deb4317267e2d38cec9fd2f58a9afa39c9f5e35f5678694ff50100004c695221033e4cc6b6ee8d8ce3335fed6d4917b2bbbac0f5743b2ced101ba036f95c51e59421023147410ce15e0a31c2bb970cdf01517266dc2c9182f5938636ed363cfd4cc3ae210342a3c8a4b20c7a122a011a07063df04e4c5ad520a1302a2a66e174fd9b0d4ea453ae'; var scriptHex = '00483045022100aac57f3ba004e6265097b759d92132c43fb5dcb033c2a3f6e61caa5e05e6b97e02200dae579e54c8e733d222eae5bbbaf557bbcf03271cf76775c91744c24a99916b014c69522103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e453ae';
//decoded: 3045022002da7fae9b98615115b7e9a4f1d9efd581463b670f91ec6404a14cb6fc9c4531022100ff449d72ba4e72deb4317267e2d38cec9fd2f58a9afa39c9f5e35f5678694ff501 0 0 5221033e4cc6b6ee8d8ce3335fed6d4917b2bbbac0f5743b2ced101ba036f95c51e59421023147410ce15e0a31c2bb970cdf01517266dc2c9182f5938636ed363cfd4cc3ae210342a3c8a4b20c7a122a011a07063df04e4c5ad520a1302a2a66e174fd9b0d4ea453ae //decoded: 0 3045022100aac57f3ba004e6265097b759d92132c43fb5dcb033c2a3f6e61caa5e05e6b97e02200dae579e54c8e733d222eae5bbbaf557bbcf03271cf76775c91744c24a99916b01 522103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e453ae
//meaning: sig place_holder place_holder serialized_script //meaning: sig place_holder place_holder serialized_script
var scriptBuf = new Buffer(scriptHex, 'hex'); var scriptBuf = new Buffer(scriptHex, 'hex');
var script = new Script(scriptBuf); var script = new Script(scriptBuf);
@ -53,25 +53,6 @@ describe('Script', function() {
}); });
}); });
describe('#removePlaceHolders', function() {
it('should remove place holders from this script', function() {
var scriptHex = '483045022002da7fae9b98615115b7e9a4f1d9efd581463b670f91ec6404a14cb6fc9c4531022100ff449d72ba4e72deb4317267e2d38cec9fd2f58a9afa39c9f5e35f5678694ff50100004c695221033e4cc6b6ee8d8ce3335fed6d4917b2bbbac0f5743b2ced101ba036f95c51e59421023147410ce15e0a31c2bb970cdf01517266dc2c9182f5938636ed363cfd4cc3ae210342a3c8a4b20c7a122a011a07063df04e4c5ad520a1302a2a66e174fd9b0d4ea453ae';
//decoded: 3045022002da7fae9b98615115b7e9a4f1d9efd581463b670f91ec6404a14cb6fc9c4531022100ff449d72ba4e72deb4317267e2d38cec9fd2f58a9afa39c9f5e35f5678694ff501 0 0 5221033e4cc6b6ee8d8ce3335fed6d4917b2bbbac0f5743b2ced101ba036f95c51e59421023147410ce15e0a31c2bb970cdf01517266dc2c9182f5938636ed363cfd4cc3ae210342a3c8a4b20c7a122a011a07063df04e4c5ad520a1302a2a66e174fd9b0d4ea453ae
//meaning: sig place_holder place_holder serialized_script
var scriptBuf = new Buffer(scriptHex, 'hex');
var script = new Script(scriptBuf);
script.removePlaceHolders();
var scriptHex2 = '483045022002da7fae9b98615115b7e9a4f1d9efd581463b670f91ec6404a14cb6fc9c4531022100ff449d72ba4e72deb4317267e2d38cec9fd2f58a9afa39c9f5e35f5678694ff5014c695221033e4cc6b6ee8d8ce3335fed6d4917b2bbbac0f5743b2ced101ba036f95c51e59421023147410ce15e0a31c2bb970cdf01517266dc2c9182f5938636ed363cfd4cc3ae210342a3c8a4b20c7a122a011a07063df04e4c5ad520a1302a2a66e174fd9b0d4ea453ae';
//decoded: 3045022002da7fae9b98615115b7e9a4f1d9efd581463b670f91ec6404a14cb6fc9c4531022100ff449d72ba4e72deb4317267e2d38cec9fd2f58a9afa39c9f5e35f5678694ff501 5221033e4cc6b6ee8d8ce3335fed6d4917b2bbbac0f5743b2ced101ba036f95c51e59421023147410ce15e0a31c2bb970cdf01517266dc2c9182f5938636ed363cfd4cc3ae210342a3c8a4b20c7a122a011a07063df04e4c5ad520a1302a2a66e174fd9b0d4ea453ae
//meaning: sig serialized_script
var scriptBuf2 = new Buffer(scriptHex2, 'hex');
var script2 = new Script(scriptBuf2);
script.buffer.toString('hex').should.equal(script2.buffer.toString('hex'));
});
});
describe('prependOp0', function() { describe('prependOp0', function() {
it('should prepend the script with OP_0', function() { it('should prepend the script with OP_0', function() {
var scriptHex = '483045022002da7fae9b98615115b7e9a4f1d9efd581463b670f91ec6404a14cb6fc9c4531022100ff449d72ba4e72deb4317267e2d38cec9fd2f58a9afa39c9f5e35f5678694ff50100004c695221033e4cc6b6ee8d8ce3335fed6d4917b2bbbac0f5743b2ced101ba036f95c51e59421023147410ce15e0a31c2bb970cdf01517266dc2c9182f5938636ed363cfd4cc3ae210342a3c8a4b20c7a122a011a07063df04e4c5ad520a1302a2a66e174fd9b0d4ea453ae'; var scriptHex = '483045022002da7fae9b98615115b7e9a4f1d9efd581463b670f91ec6404a14cb6fc9c4531022100ff449d72ba4e72deb4317267e2d38cec9fd2f58a9afa39c9f5e35f5678694ff50100004c695221033e4cc6b6ee8d8ce3335fed6d4917b2bbbac0f5743b2ced101ba036f95c51e59421023147410ce15e0a31c2bb970cdf01517266dc2c9182f5938636ed363cfd4cc3ae210342a3c8a4b20c7a122a011a07063df04e4c5ad520a1302a2a66e174fd9b0d4ea453ae';
@ -149,5 +130,29 @@ describe('Script', function() {
}); });
describe('#countMissingSignatures', function() {
Script = ScriptModule;
it('should count missing signature in empty scripts', function() {
var s = new Script();
s.countMissingSignatures().should.equal(1);
});
it('should count missing signatures p2sh 2-3 1 missing', function() {
// from https://gist.github.com/matiu/11182987
var b = new Buffer('00483045022100aac57f3ba004e6265097b759d92132c43fb5dcb033c2a3f6e61caa5e05e6b97e02200dae579e54c8e733d222eae5bbbaf557bbcf03271cf76775c91744c24a99916b014c69522103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e453ae', 'hex');
var s = new Script(b);
s.countMissingSignatures().should.equal(1);
s.countSignatures().should.equal(1);
});
it('should count missing signatures p2sh 2-3 0 missing', function() {
// from https://gist.github.com/matiu/11182987
var b = new Buffer('00483045022100aac57f3ba004e6265097b759d92132c43fb5dcb033c2a3f6e61caa5e05e6b97e02200dae579e54c8e733d222eae5bbbaf557bbcf03271cf76775c91744c24a99916b01483045022100a505aff6a1d9cc14d0658a99ebcf1901b5c9f9e6408055fa9b9da443c80bfdb602207f0391c98abecc93bc3b353c55ada4d3fb6d4bab48fd63ae184df1af367cee46014c69522103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e453ae', 'hex');
var s = new Script(b);
s.countMissingSignatures().should.equal(0);
s.countSignatures().should.equal(2);
});
});
}); });

View File

@ -8,10 +8,19 @@ var should = chai.should();
var TransactionBuilder = bitcore.TransactionBuilder; var TransactionBuilder = bitcore.TransactionBuilder;
var WalletKey = bitcore.WalletKey; var WalletKey = bitcore.WalletKey;
var Script = bitcore.Script;
var util = bitcore.util; var util = bitcore.util;
var networks = bitcore.networks; var networks = bitcore.networks;
var testdata = testdata || require('./testdata'); var testdata = testdata || require('./testdata');
var vopts = {
verifyP2SH: true,
dontVerifyStrictEnc: true
};
describe('TransactionBuilder', function() { describe('TransactionBuilder', function() {
it('should initialze the main object', function() { it('should initialze the main object', function() {
should.exist(TransactionBuilder); should.exist(TransactionBuilder);
@ -224,7 +233,7 @@ describe('TransactionBuilder', function() {
.setOutputs(outs); .setOutputs(outs);
}; };
it('should sign a tx (case 1)', function() { it('should sign a tx (case 1)', function(done) {
var b = getBuilder3(); var b = getBuilder3();
b.isFullySigned().should.equal(false); b.isFullySigned().should.equal(false);
@ -236,9 +245,18 @@ describe('TransactionBuilder', function() {
tx.isComplete().should.equal(true); tx.isComplete().should.equal(true);
tx.ins.length.should.equal(1); tx.ins.length.should.equal(1);
tx.outs.length.should.equal(2); tx.outs.length.should.equal(2);
var shex = testdata.dataUnspentSign.unspent[0].scriptPubKey;
var s = new Script(new Buffer(shex,'hex'));
tx.verifyInput(0,s, vopts, function(err, results){
should.not.exist(err);
should.exist(results);
results.should.equal(true);
done();
});
}); });
it('should sign a tx (case 2)', function() { it('should sign a tx (case 2)', function(done) {
var b = getBuilder3([{ var b = getBuilder3([{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 16 amount: 16
@ -250,6 +268,16 @@ describe('TransactionBuilder', function() {
tx.isComplete().should.equal(true); tx.isComplete().should.equal(true);
tx.ins.length.should.equal(3); tx.ins.length.should.equal(3);
tx.outs.length.should.equal(2); tx.outs.length.should.equal(2);
var shex = testdata.dataUnspentSign.unspent[0].scriptPubKey;
var s = new Script(new Buffer(shex,'hex'));
tx.verifyInput(0,s, vopts, function(err, results){
should.not.exist(err);
should.exist(results);
results.should.equal(true);
done();
});
}); });
it('should sign an incomplete tx', function() { it('should sign an incomplete tx', function() {
@ -273,7 +301,7 @@ describe('TransactionBuilder', function() {
}); });
it('should sign a tx in multiple steps (case1)', function() { it('should sign a tx in multiple steps (case1)', function(done) {
var b = getBuilder3([{ var b = getBuilder3([{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
@ -292,9 +320,20 @@ describe('TransactionBuilder', function() {
b.sign(k23); b.sign(k23);
b.isFullySigned().should.equal(true); b.isFullySigned().should.equal(true);
(b.build()).isComplete().should.equal(true); (b.build()).isComplete().should.equal(true);
var tx = b.build();
var shex = testdata.dataUnspentSign.unspent[0].scriptPubKey;
var s = new Script(new Buffer(shex,'hex'));
tx.verifyInput(0,s, vopts, function(err, results){
should.not.exist(err);
should.exist(results);
results.should.equal(true);
done();
});
}); });
it('#sign should sign a tx in multiple steps (case2)', function() { it('#sign should sign a tx in multiple steps (case2)', function(done) {
var b = getBuilder3([{ var b = getBuilder3([{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 16 amount: 16
@ -304,21 +343,67 @@ describe('TransactionBuilder', function() {
(b.build()).isComplete().should.equal(false); (b.build()).isComplete().should.equal(false);
var k1 = testdata.dataUnspentSign.keyStrings.slice(0, 1); var k1 = testdata.dataUnspentSign.keyStrings.slice(0, 1);
var k2 = testdata.dataUnspentSign.keyStrings.slice(1, 2);
var k3 = testdata.dataUnspentSign.keyStrings.slice(2, 3);
b.sign(k1); b.sign(k1);
b.isFullySigned().should.equal(false); b.isFullySigned().should.equal(false);
(b.build()).isComplete().should.equal(false); (b.build()).isComplete().should.equal(false);
var k2 = testdata.dataUnspentSign.keyStrings.slice(1, 2);
b.sign(k2); b.sign(k2);
b.isFullySigned().should.equal(false); b.isFullySigned().should.equal(false);
(b.build()).isComplete().should.equal(false); (b.build()).isComplete().should.equal(false);
var k3 = testdata.dataUnspentSign.keyStrings.slice(2, 3);
b.sign(k3); b.sign(k3);
b.isFullySigned().should.equal(true); b.isFullySigned().should.equal(true);
(b.build()).isComplete().should.equal(true); (b.build()).isComplete().should.equal(true);
var tx = b.build();
var shex = testdata.dataUnspentSign.unspent[0].scriptPubKey;
var s = new Script(new Buffer(shex,'hex'));
tx.verifyInput(0,s, vopts, function(err, results){
should.not.exist(err);
should.exist(results);
results.should.equal(true);
done();
});
}); });
it('#sign should sign a tx in multiple steps (case2) / diff order', function(done) {
var b = getBuilder3([{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 16
}]);
b.isFullySigned().should.equal(false);
(b.build()).isComplete().should.equal(false);
var k2 = testdata.dataUnspentSign.keyStrings.slice(0, 1);
var k3 = testdata.dataUnspentSign.keyStrings.slice(1, 2);
var k1 = testdata.dataUnspentSign.keyStrings.slice(2, 3);
b.sign(k1);
b.isFullySigned().should.equal(false);
(b.build()).isComplete().should.equal(false);
b.sign(k2);
b.isFullySigned().should.equal(false);
(b.build()).isComplete().should.equal(false);
b.sign(k3);
b.isFullySigned().should.equal(true);
(b.build()).isComplete().should.equal(true);
var tx = b.build();
var shex = testdata.dataUnspentSign.unspent[0].scriptPubKey;
var s = new Script(new Buffer(shex,'hex'));
tx.verifyInput(0,s, vopts, function(err, results){
should.not.exist(err);
should.exist(results);
results.should.equal(true);
done();
});
});
it('should generate dynamic fee and readjust (and not) the selected UTXOs (case1)', function() { it('should generate dynamic fee and readjust (and not) the selected UTXOs (case1)', function() {
//this cases exceeds the input by 1mbtc AFTEr calculating the dynamic fee, //this cases exceeds the input by 1mbtc AFTEr calculating the dynamic fee,
//so, it should trigger adding a new 10BTC utxo //so, it should trigger adding a new 10BTC utxo
@ -394,7 +479,7 @@ describe('TransactionBuilder', function() {
tx.isComplete().should.equal(false); tx.isComplete().should.equal(false);
}); });
it('should sign a p2pubkey tx', function() { it('should sign a p2pubkey tx', function(done) {
var opts = { var opts = {
remainderOut: {address: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd'}, remainderOut: {address: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd'},
}; };
@ -413,10 +498,21 @@ describe('TransactionBuilder', function() {
tx.isComplete().should.equal(true); tx.isComplete().should.equal(true);
tx.ins.length.should.equal(1); tx.ins.length.should.equal(1);
tx.outs.length.should.equal(2); tx.outs.length.should.equal(2);
var tx = b.build();
var shex = testdata.dataUnspentSign.unspentPubKey[0].scriptPubKey;
var s = new Script(new Buffer(shex,'hex'));
tx.verifyInput(0,s, vopts, function(err, results){
should.not.exist(err);
should.exist(results);
results.should.equal(true);
done();
});
}); });
it('should sign a multisig tx', function() { it('should sign a multisig tx', function(done) {
var opts = { var opts = {
remainderOut: {address: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd'}, remainderOut: {address: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd'},
}; };
@ -434,10 +530,20 @@ describe('TransactionBuilder', function() {
tx.ins.length.should.equal(1); tx.ins.length.should.equal(1);
tx.outs.length.should.equal(2); tx.outs.length.should.equal(2);
tx.isComplete().should.equal(true); tx.isComplete().should.equal(true);
var tx = b.build();
var shex = testdata.dataUnspentSign.unspentMulti[0].scriptPubKey;
var s = new Script(new Buffer(shex,'hex'));
tx.verifyInput(0,s, vopts, function(err, results){
should.not.exist(err);
should.exist(results);
results.should.equal(true);
done();
});
}); });
it('should sign a multisig tx in steps (3-5)', function() { it('should sign a multisig tx in steps (3-5)', function(done) {
var opts = { var opts = {
remainderOut: {address: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd'}, remainderOut: {address: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd'},
}; };
@ -464,6 +570,17 @@ describe('TransactionBuilder', function() {
tx.ins.length.should.equal(1); tx.ins.length.should.equal(1);
tx.outs.length.should.equal(2); tx.outs.length.should.equal(2);
tx.isComplete().should.equal(true); tx.isComplete().should.equal(true);
var tx = b.build();
var shex = testdata.dataUnspentSign.unspentMulti[0].scriptPubKey;
var s = new Script(new Buffer(shex,'hex'));
tx.verifyInput(0,s, vopts, function(err, results){
should.not.exist(err);
should.exist(results);
results.should.equal(true);
done();
});
}); });
@ -484,29 +601,28 @@ describe('TransactionBuilder', function() {
var k3 = testdata.dataUnspentSign.keyStringsMulti.slice(2,3); var k3 = testdata.dataUnspentSign.keyStringsMulti.slice(2,3);
var tx = b.build(); var tx = b.build();
b.isFullySigned().should.equal(false); b.isFullySigned().should.equal(false);
// This is cumbersome. Before sign, missing is 1. Need to be changed in the future tx.countInputSignatures(0).should.equal(0);
tx.countInputMissingSignatures(0).should.equal(1);
b.sign(['cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV']);
tx.countInputMissingSignatures(0).should.equal(1);
b.sign(['cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV']);
tx.countInputSignatures(0).should.equal(0);
b.sign(k1); b.sign(k1);
tx.countInputMissingSignatures(0).should.equal(2); tx.countInputSignatures(0).should.equal(1);
b.isFullySigned().should.equal(false); b.isFullySigned().should.equal(false);
b.sign(k2); b.sign(k2);
tx.countInputMissingSignatures(0).should.equal(1); tx.countInputSignatures(0).should.equal(2);
b.isFullySigned().should.equal(false); b.isFullySigned().should.equal(false);
b.sign(k3); b.sign(k3);
tx.countInputMissingSignatures(0).should.equal(0); tx.countInputSignatures(0).should.equal(3);
b.isFullySigned().should.equal(true); b.isFullySigned().should.equal(true);
}); });
it('should avoid siging with the same key twice multisig signs (3-5)', function() { it('should avoid siging with the same key twice multisig signs (3-5)', function(done) {
var opts = { var opts = {
remainderOut: {address: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd'}, remainderOut: {address: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd'},
}; };
@ -522,19 +638,31 @@ describe('TransactionBuilder', function() {
var k23 = testdata.dataUnspentSign.keyStringsMulti.slice(1,3); var k23 = testdata.dataUnspentSign.keyStringsMulti.slice(1,3);
var tx = b.build(); var tx = b.build();
tx.countInputMissingSignatures(0).should.equal(1); tx.countInputSignatures(0).should.equal(0);
b.sign(k1); b.sign(k1);
b.isFullySigned().should.equal(false); b.isFullySigned().should.equal(false);
tx.countInputMissingSignatures(0).should.equal(2); tx.countInputSignatures(0).should.equal(1);
b.sign(k1); b.sign(k1);
b.isFullySigned().should.equal(false); b.isFullySigned().should.equal(false);
tx.countInputMissingSignatures(0).should.equal(2); tx.countInputSignatures(0).should.equal(1);
b.sign(k1); b.sign(k1);
b.isFullySigned().should.equal(false); b.isFullySigned().should.equal(false);
tx.countInputMissingSignatures(0).should.equal(2); tx.countInputSignatures(0).should.equal(1);
b.sign(k23); b.sign(k23);
b.isFullySigned().should.equal(true); b.isFullySigned().should.equal(true);
tx.countInputMissingSignatures(0).should.equal(0); tx.countInputSignatures(0).should.equal(3);
var tx = b.build();
var shex = testdata.dataUnspentSign.unspentMulti[0].scriptPubKey;
var s = new Script(new Buffer(shex,'hex'));
tx.verifyInput(0,s, vopts, function(err, results){
should.not.exist(err);
should.exist(results);
results.should.equal(true);
done();
});
}); });
@ -553,6 +681,16 @@ describe('TransactionBuilder', function() {
}; };
}; };
//
// bitcoind createmultisig 3 '["03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d" , "0380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127" , "0392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed03", "03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3" , "03e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e4" ]'
//
// =>
//
// {
// "address" : "2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6",
// "redeemScript" : "532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae"
// }
//
var getP2shBuilder = function(setMap) { var getP2shBuilder = function(setMap) {
var network = 'testnet'; var network = 'testnet';
var opts = { var opts = {
@ -564,7 +702,7 @@ describe('TransactionBuilder', function() {
var info = TransactionBuilder.infoForP2sh(p2shOpts, network); var info = TransactionBuilder.infoForP2sh(p2shOpts, network);
var outs = outs || [{ var outs = outs || [{
address: info.address, address: 'mon1Hqs3jqKTtRSnRwJ3pRYMFos9WYfKb5',
amount: 0.08 amount: 0.08
}]; }];
var b = new TransactionBuilder(opts) var b = new TransactionBuilder(opts)
@ -584,16 +722,34 @@ describe('TransactionBuilder', function() {
(function() {b.sign(testdata.dataUnspentSign.keyStringsP2sh);}).should.throw(); (function() {b.sign(testdata.dataUnspentSign.keyStringsP2sh);}).should.throw();
}); });
it('should sign a p2sh/multisign tx', function() {
var b = getP2shBuilder(1); var _checkOK = function(b, done) {
b.sign(testdata.dataUnspentSign.keyStringsP2sh);
b.isFullySigned().should.equal(true); b.isFullySigned().should.equal(true);
var tx = b.build(); var tx = b.build();
tx.ins.length.should.equal(1); tx.ins.length.should.equal(1);
tx.outs.length.should.equal(2); tx.outs.length.should.equal(2);
tx.isComplete().should.equal(true); tx.isComplete().should.equal(true);
});
var shex = testdata.dataUnspentSign.unspentP2sh[0].scriptPubKey;
var s = new Script(new Buffer(shex,'hex'));
tx.verifyInput(0,s, vopts, function(err, results){
should.exist(results);
results.should.equal(true);
should.not.exist(err);
done();
});
};
[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,2,1],[3,1,2]].forEach(function(order) {
it('should sign a p2sh/multisig tx in order ' + order.join(','), function(done) {
var b = getP2shBuilder(1);
b.sign([testdata.dataUnspentSign.keyStringsP2sh[3]]);
b.sign([testdata.dataUnspentSign.keyStringsP2sh[1]]);
b.sign([testdata.dataUnspentSign.keyStringsP2sh[2]]);
_checkOK(b, done);
});
});
it('should sign in steps a p2sh/multisign tx', function() { it('should sign in steps a p2sh/multisign tx', function() {
var b = getP2shBuilder(1); var b = getP2shBuilder(1);
@ -818,11 +974,12 @@ describe('TransactionBuilder', function() {
amount: 16 amount: 16
}]) }])
.sign(testdata.dataUnspentSign.keyStrings); .sign(testdata.dataUnspentSign.keyStrings);
(function() {b2.merge(b);}).should.throw(); b2.isFullySigned().should.equal(true);
b2.merge(b, true); b2.merge(b);
b2.isFullySigned().should.equal(true);
}); });
it('#merge p2sh/steps', function() { it('#merge p2sh/steps', function(done) {
var b = getP2shBuilder(1); var b = getP2shBuilder(1);
var k1 = testdata.dataUnspentSign.keyStringsP2sh.slice(0,1); var k1 = testdata.dataUnspentSign.keyStringsP2sh.slice(0,1);
var k2 = testdata.dataUnspentSign.keyStringsP2sh.slice(1,2); var k2 = testdata.dataUnspentSign.keyStringsP2sh.slice(1,2);
@ -835,6 +992,9 @@ describe('TransactionBuilder', function() {
var tx = b.build(); var tx = b.build();
tx.isComplete().should.equal(false); tx.isComplete().should.equal(false);
b = TransactionBuilder.fromObj(b.toObj());
// TODO TO OBJ!
var b2 = getP2shBuilder(1); var b2 = getP2shBuilder(1);
b2.sign(k2); b2.sign(k2);
b2.signaturesAdded.should.equal(1); b2.signaturesAdded.should.equal(1);
@ -843,6 +1003,7 @@ describe('TransactionBuilder', function() {
tx = b2.build(); tx = b2.build();
tx.isComplete().should.equal(false); tx.isComplete().should.equal(false);
b2 = TransactionBuilder.fromObj(b2.toObj());
var b3 = getP2shBuilder(1); var b3 = getP2shBuilder(1);
b3.sign(k3); b3.sign(k3);
b3.signaturesAdded.should.equal(1); b3.signaturesAdded.should.equal(1);
@ -851,9 +1012,20 @@ describe('TransactionBuilder', function() {
tx = b3.build(); tx = b3.build();
tx.isComplete().should.equal(true); tx.isComplete().should.equal(true);
b3 = TransactionBuilder.fromObj(b3.toObj());
b2.merge(b3); b2.merge(b3);
b2.signaturesAdded.should.equal(3); b2.signaturesAdded.should.equal(3);
tx = b2.build(); tx = b2.build();
tx.isComplete().should.equal(true); tx.isComplete().should.equal(true);
var shex = testdata.dataUnspentSign.unspentP2sh[0].scriptPubKey;
var s = new Script(new Buffer(shex,'hex'));
tx.verifyInput(0,s, vopts, function(err, results){
should.exist(results);
results.should.equal(true);
should.not.exist(err);
done();
});
}); });
}); });