add p2sh support and test

This commit is contained in:
Matias Alejo Garcia 2014-04-02 00:59:26 -03:00
parent d8f49e87ae
commit f6f7a01efc
8 changed files with 378 additions and 147 deletions

View File

@ -490,7 +490,25 @@ Script.prototype.toHumanReadable = function() {
}
}
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) {

View File

@ -552,28 +552,28 @@ Transaction.prototype.getSize = function getHash() {
return this.size;
};
Transaction.prototype.countInputMissingSignatures = function(index) {
var ret = 0;
var script = new Script(this.ins[index].s);
return script.countMissingSignatures();
};
Transaction.prototype.isInputComplete = function(index) {
return this.countInputMissingSignatures(index)===0;
};
Transaction.prototype.isComplete = function() {
var ret = true;
var l = this.ins.length;
for (var i = 0; i < l; i++) {
var script = new Script(this.ins[i].s);
// Multisig?
if (!Buffer.isBuffer(script.chunks[0]) && script.chunks[0] ===0) {
for (var i = 1; i < script.chunks.length; i++) {
if (buffertools.compare(script.chunks[i], util.EMPTY_BUFFER) === 0){
ret = false;
break;
}
}
if (!this.isInputComplete(i)){
ret = false;
break;
}
else {
if (buffertools.compare(this.ins[i].s, util.EMPTY_BUFFER) === 0) {
ret = false;
break;
}
}
};
}
return ret;
};

View File

@ -115,12 +115,12 @@ function TransactionBuilder(opts) {
}
/*
* _scriptForAddress
* scriptForAddress
*
* Returns a scriptPubKey for the given address type
*/
TransactionBuilder._scriptForAddress = function(addressString) {
TransactionBuilder.scriptForAddress = function(addressString) {
var livenet = networks.livenet;
var testnet = networks.testnet;
@ -154,7 +154,7 @@ TransactionBuilder._scriptForPubkeys = function(out) {
TransactionBuilder._scriptForOut = function(out) {
var ret;
if (out.address)
ret = this._scriptForAddress(out.address);
ret = this.scriptForAddress(out.address);
else if (out.pubkeys || out.nreq || out.nreq > 1)
ret = this._scriptForPubkeys(out);
else
@ -201,8 +201,7 @@ TransactionBuilder.prototype._setInputMap = function() {
' Type:' + scriptPubKey.getRawOutType());
inputMap.push({
address: utxo.address, //TODO que pasa en multisig normal?
scriptPubKeyHex: utxo.scriptPubKey,
address: utxo.address,
scriptPubKey: scriptPubKey,
scriptType: scriptType,
i: i,
@ -394,7 +393,6 @@ TransactionBuilder.prototype.setOutputs = function(outs) {
};
TransactionBuilder._mapKeys = function(keys) {
//prepare keys
var walletKeyMap = {};
var l = keys.length;
@ -437,10 +435,43 @@ TransactionBuilder.prototype._checkTx = function() {
throw new Error('tx is not defined');
};
TransactionBuilder.prototype._multiFindKey = function(walletKeyMap,pubKeyHash) {
var wk;
[ networks.livenet, networks.testnet].forEach(function(n) {
[ n.addressPubkey, n.addressScript].forEach(function(v) {
var a = new Address(v,pubKeyHash);
if (!wk && walletKeyMap[a]) {
wk = walletKeyMap[a];
}
});
});
return wk;
};
TransactionBuilder.prototype._findWalletKey = function(walletKeyMap, input) {
var wk;
if (input.address) {
wk = walletKeyMap[input.address];
}
else if (input.pubKeyHash) {
wk = this._multiFindKey(walletKeyMap, input.pubKeyHash);
}
else if (input.pubKeyBuf) {
var pubKeyHash = util.sha256ripe160(input.pubKeyBuf);
wk = this._multiFindKey(walletKeyMap, pubKeyHash);
} else {
throw new Error('no infomation at input to find keys');
}
return wk;
};
TransactionBuilder.prototype._signPubKey = function(walletKeyMap, input, txSigHash) {
if (this.tx.ins[input.i].s.length > 0) return {};
var wk = walletKeyMap[input.address];
var wk = this._findWalletKey(walletKeyMap, input);
if (!wk) return;
var sigRaw = TransactionBuilder._signHashAndVerify(wk, txSigHash);
@ -451,14 +482,14 @@ TransactionBuilder.prototype._signPubKey = function(walletKeyMap, input, txSigHa
var scriptSig = new Script();
scriptSig.chunks.push(sig);
scriptSig.updateBuffer();
return {isFullySigned: true, script: scriptSig.getBuffer()};
return {isFullySigned: true, signaturesAdded: true, script: scriptSig.getBuffer()};
};
TransactionBuilder.prototype._signPubKeyHash = function(walletKeyMap, input, txSigHash) {
if (this.tx.ins[input.i].s.length > 0) return {};
var wk = walletKeyMap[input.address];
var wk = this._findWalletKey(walletKeyMap, input);
if (!wk) return;
var sigRaw = TransactionBuilder._signHashAndVerify(wk, txSigHash);
@ -470,18 +501,16 @@ TransactionBuilder.prototype._signPubKeyHash = function(walletKeyMap, input, txS
scriptSig.chunks.push(sig);
scriptSig.chunks.push(wk.privKey.public);
scriptSig.updateBuffer();
return {isFullySigned: true, script: scriptSig.getBuffer()};
return {isFullySigned: true, signaturesAdded: true, script: scriptSig.getBuffer()};
};
// FOR TESTING
/*
var _dumpChunks = function (scriptSig, label) {
console.log('## DUMP: ' + label + ' ##');
for(var i=0; i<scriptSig.chunks.length; i++) {
console.log('\tCHUNK ', i, scriptSig.chunks[i]);
}
};
*/
// var _dumpChunks = function (scriptSig, label) {
// console.log('## DUMP: ' + label + ' ##');
// for(var i=0; i<scriptSig.chunks.length; i++) {
// console.log('\tCHUNK ', i, scriptSig.chunks[i]);
// }
// };
TransactionBuilder.prototype._initMultiSig = function(scriptSig, nreq) {
var wasUpdated = false;
@ -514,6 +543,13 @@ TransactionBuilder.prototype._chunkIsEmpty = function(chunk) {
buffertools.compare(chunk, util.EMPTY_BUFFER) === 0;
};
TransactionBuilder.prototype._chunkIsSignature = function(chunk) {
return chunk.length
buffertools.compare(chunk, util.EMPTY_BUFFER) === 0;
};
TransactionBuilder.prototype._updateMultiSig = function(wk, scriptSig, txSigHash, nreq) {
var wasUpdated = this._initMultiSig(scriptSig, nreq);
@ -540,40 +576,6 @@ TransactionBuilder.prototype._updateMultiSig = function(wk, scriptSig, txSigHash
};
TransactionBuilder.prototype._multiFindKey = function(walletKeyMap,pubKeyHash) {
var wk;
[ networks.livenet, networks.testnet].forEach(function(n) {
[ n.addressPubkey, n.addressScript].forEach(function(v) {
var a = new Address(v,pubKeyHash);
if (!wk && walletKeyMap[a]) {
wk = walletKeyMap[a];
}
});
});
return wk;
};
TransactionBuilder.prototype._countMultiSig = function(script) {
var nsigs = 0;
for (var i = 1; i < script.chunks.length; i++)
if (!this._chunkIsEmpty(script.chunks[i]))
nsigs++;
return nsigs;
};
TransactionBuilder.prototype.countInputMultiSig = function(i) {
var s = new Script(this.tx.ins[i].s);
if (!s.chunks.length || s.chunks[0] !== 0)
return 0; // does not seems multisig
return this._countMultiSig(s);
};
TransactionBuilder.prototype._signMultiSig = function(walletKeyMap, input, txSigHash) {
var pubkeys = input.scriptPubKey.capture(),
nreq = input.scriptPubKey.chunks[0] - 80, //see OP_2-OP_16
@ -581,76 +583,96 @@ TransactionBuilder.prototype._signMultiSig = function(walletKeyMap, input, txSig
originalScriptBuf = this.tx.ins[input.i].s;
var scriptSig = new Script (originalScriptBuf);
var signaturesAdded = false;
for(var j=0; j<l && this._countMultiSig(scriptSig)<nreq; j++) {
var pubKeyHash = util.sha256ripe160(pubkeys[j]);
var wk = this._multiFindKey(walletKeyMap, pubKeyHash);
for(var j=0; j<l && scriptSig.countMissingSignatures(); j++) {
var wk = this._findWalletKey(walletKeyMap, {pubKeyBuf: pubkeys[j]});
if (!wk) continue;
var newScriptSig = this._updateMultiSig(wk, scriptSig, txSigHash, nreq);
if (newScriptSig)
if (newScriptSig) {
scriptSig = newScriptSig;
signaturesAdded = true;
}
}
return {
isFullySigned: this._countMultiSig(scriptSig) === nreq,
isFullySigned: scriptSig.countMissingSignatures() === 0,
signaturesAdded: signaturesAdded,
script: scriptSig.getBuffer(),
};
};
var fnToSign = {};
TransactionBuilder.prototype._scriptIsAppended = function(script, scriptToAddBuf) {
var len = script.chunks.length;
if (script.chunks[len-1] === undefined)
return false;
if (typeof script.chunks[len-1] === 'number')
return false;
if (buffertools.compare(script.chunks[len-1] , scriptToAddBuf) !==0 )
return false;
return true;
};
TransactionBuilder.prototype._addScript = function(scriptBuf, scriptToAddBuf) {
var s = new Script(scriptBuf);
if (!this._scriptIsAppended(s, scriptToAddBuf)) {
s.chunks.push(scriptToAddBuf);
s.updateBuffer();
}
return s.getBuffer();
};
TransactionBuilder.prototype._getInputForP2sh = function(script, index) {
var scriptType = script.classify();
// pubKeyHash is needed for TX_PUBKEYHASH and TX_PUBKEY to retrieve the keys.
var pubKeyHash;
switch(scriptType) {
case Script.TX_PUBKEYHASH:
pubKeyHash = script.captureOne();
break;
case Script.TX_PUBKEY:
var chunk = script.captureOne();
pubKeyHash = util.sha256ripe160(chunk);
}
return {
i: index,
pubKeyHash: pubKeyHash,
scriptPubKey: script,
scriptType: scriptType,
};
};
TransactionBuilder.prototype._signScriptHash = function(walletKeyMap, input, txSigHash) {
var originalScriptBuf = this.tx.ins[input.i].s;
if (!this.hashToScriptMap)
throw new Error('hashToScriptMap not set');
var scriptHex = this.hashToScriptMap[input.address];
if (!scriptHex) return;
var script = new Script(new Buffer(scriptHex,'hex'));
var scriptType = script.classify();
var scriptPubKeyHex = script.getBuffer().toString('hex');
var scriptBuf = new Buffer(scriptHex,'hex');
var script = new Script(scriptBuf);
var scriptType = script.classify();
if (!fnToSign[scriptType])
throw new Error('dont know how to sign p2sh script type'+ script.getRawOutType());
if (!fnToSign[scriptType] || scriptType === Script.TX_SCRIPTHASH)
throw new Error('dont know how to sign p2sh script type:'+ script.getRawOutType());
var newInput = {
address: 'TODO', // if p2pkubkeyhash -> get the address
i: input.i,
scriptPubKey: script,
scriptPubKeyHex: scriptPubKeyHex ,
scriptType: scriptType,
};
var newInput = this._getInputForP2sh(script, input.i);
var newTxSigHash = this.tx.hashForSignature( script, newInput.i, this.signhash);
var ret = fnToSign[scriptType].call(this, walletKeyMap, newInput, newTxSigHash);
var txSigHash2 = this.tx.hashForSignature( script, input.i, this.signhash);
var ret = fnToSign[scriptType].call(this, walletKeyMap, newInput, txSigHash2);
var rc =1; //TODO : si alguno firmó...
if (ret.script) {
console.log('[TransactionBuilder.js.634] IN'); //TODO
var scriptSig = new Script(originalScriptBuf);
var len = scriptSig.chunks.length;
var scriptBufNotAlreadyAppended = scriptSig.chunks[len-1] !== undefined && (typeof scriptSig.chunks[len-1] == "number" || scriptSig.chunks[len-1].toString('hex') != scriptBuf.toString('hex'));
if (rc > 0 && scriptBufNotAlreadyAppended) {
scriptSig.chunks.push(scriptBuf);
scriptSig.updateBuffer();
ret.script = scriptSig.getBuffer();
}
if (scriptType == Script.TX_MULTISIG && scriptSig.finishedMultiSig())
{
scriptSig.removePlaceHolders();
scriptSig.prependOp0();
ret.script = scriptSig.getBuffer();
}
if (ret && ret.script && ret.signaturesAdded) {
ret.script = this._addScript(ret.script, scriptBuf);
}
return ret;
};
@ -676,17 +698,16 @@ TransactionBuilder.prototype.sign = function(keys) {
var ret = fnToSign[input.scriptType].call(this, walletKeyMap, input, txSigHash);
if (ret && ret.script) {
tx.ins[i].s = ret.script; //esto no aqui TODO
tx.ins[i].s = ret.script;
if (ret.isFullySigned) this.inputsSigned++;
}
}
return this;
};
// [addr -> script]
// [ { address:scriptHex }]
TransactionBuilder.prototype.setHashToScriptMap = function(hashToScriptMap) {
this.hashToScriptMap= hashToScriptMap;
return this;
};

View File

@ -17,11 +17,11 @@ var run = function() {
var utxos = [
{
address: input.addr,
txid: "a2a1b0bfbbe769253787d83c097adf61e6d77088e295249e9c3f1ca8a035c639",
vout: 0,
txid: "39c71ebda371f75f4b854a720eaf9898b237facf3c2b101b58cd4383a44a6adc",
vout: 1,
ts: 1396288753,
scriptPubKey: "76a914e867aad8bd361f57c50adc37a0c018692b5b0c9a88ac",
amount: 0.63,
amount: 0.4296,
confirmations: 2
}
];
@ -50,7 +50,7 @@ var run = function() {
.build();
var txHex = tx.serialize().toString('hex');
console.log('1) SEND TO MULSISIG TX: ', txHex);
console.log('[this example originally generated TXID: ff5c8b4912f6d056f0cf8431ec27032a73df22c167726267dd4cc0d7817a1e7d on testnet]\n\n\thttp://test.bitcore.io/tx/ff5c8b4912f6d056f0cf8431ec27032a73df22c167726267dd4cc0d7817a1e7d\n\n');
console.log('[this example originally generated TXID: e4bc22d8c519d3cf848d710619f8480be56176a4a6548dfbe865ab3886b578b5 on testnet]\n\n\thttp://test.bitcore.io/tx/e4bc22d8c519d3cf848d710619f8480be56176a4a6548dfbe865ab3886b578b5\n\n');
//save scriptPubKey
@ -63,7 +63,7 @@ var run = function() {
var utxos2 = [
{
address: input.addr,
txid: "ff5c8b4912f6d056f0cf8431ec27032a73df22c167726267dd4cc0d7817a1e7d",
txid: "e4bc22d8c519d3cf848d710619f8480be56176a4a6548dfbe865ab3886b578b5",
vout: 0,
ts: 1396288753,
scriptPubKey: scriptPubKey,
@ -84,9 +84,9 @@ var run = function() {
var txHex = tx.serialize().toString('hex');
console.log('2) REDEEM SCRIPT: ', txHex);
console.log('=> Is signed status:', b.isFullySigned(), b.countInputMultiSig(0) );
console.log('=> Is signed status:', b.isFullySigned(), tx.countInputMissingSignatures(0) );
console.log('[this example originally generated TXID: 2813c5a670d2c9d0527718f9d0ea896c78c3c8fc57b409e67308744fc7a7a98e on testnet]\n\n\thttp://test.bitcore.io/tx/2813c5a670d2c9d0527718f9d0ea896c78c3c8fc57b409e67308744fc7a7a98e');
console.log('[this example originally generated TXID: 1eb388977b2de99562eb0fbcc661a100eaffed99c53bfcfebe5a087002039b83 on testnet]\n\n\thttp://test.bitcore.io/tx/1eb388977b2de99562eb0fbcc661a100eaffed99c53bfcfebe5a087002039b83');
};

View File

@ -16,11 +16,11 @@ var run = function() {
var utxos = [
{
address: "n2hoFVbPrYQf7RJwiRy1tkbuPPqyhAEfbp",
txid: "ba20653648a896ae95005b8f52847935a7313da06cd7295bb2cfc8b5c1b36c71",
txid: "e4bc22d8c519d3cf848d710619f8480be56176a4a6548dfbe865ab3886b578b5",
vout: 1,
ts: 1396290442,
scriptPubKey: "76a914e867aad8bd361f57c50adc37a0c018692b5b0c9a88ac",
amount: 0.5298,
amount: 0.3795,
confirmations: 7
}
];
@ -57,12 +57,13 @@ var run = function() {
.setOutputs(outs)
.sign([input.priv])
.build();
var txHex = tx.serialize().toString('hex');
console.log('p2sh address: ' + p2shAddress); //TODO
console.log('1) SEND TO P2SH TX: ', txHex);
console.log('[this example originally generated TXID: 8675a1f7ab0c2eeec2ff2def539446d1942efffd468319107429b894e60ecac3 on testnet]\n\n\thttp://test.bitcore.io/tx/8675a1f7ab0c2eeec2ff2def539446d1942efffd468319107429b894e60ecac3\n\n');
console.log('## p2sh address: ' + p2shAddress); //TODO
console.log('\n1) SEND TO P2SH TX: ', txHex);
console.log('[this example originally generated TXID: c2e50d1c8c581d8c4408378b751633f7eb86687fc5f0502be7b467173f275ae7 on testnet]\n\n\thttp://test.bitcore.io/tx/c2e50d1c8c581d8c4408378b751633f7eb86687fc5f0502be7b467173f275ae7\n\n');
//save scriptPubKey
var scriptPubKey = tx.outs[0].s.toString('hex');
@ -74,12 +75,12 @@ var run = function() {
var utxos2 = [
{
address: p2shAddress,
txid: "ba20653648a896ae95005b8f52847935a7313da06cd7295bb2cfc8b5c1b36c71",
txid: "c2e50d1c8c581d8c4408378b751633f7eb86687fc5f0502be7b467173f275ae7",
vout: 0,
ts: 1396288753,
scriptPubKey: scriptPubKey,
ts: 1396375187,
scriptPubKey: scriptPubKey,
amount: 0.05,
confirmations: 2
confirmations: 1
}
];
@ -94,18 +95,42 @@ var run = function() {
.setOutputs(outs)
.sign(privs);
tx= b.build();
console.log('Builder:');
console.log('\tSignatures:' + tx.countInputMissingSignatures(0) );
console.log('\t#isFullySigned:' + b.isFullySigned() );
console.log('TX:');
console.log('\t #isComplete:' + tx.isComplete() );
var txHex = tx.serialize().toString('hex');
console.log('2) REDEEM SCRIPT: ', txHex);
console.log('=> Is signed status:', b.isFullySigned(), b.countInputMultiSig(0) );
console.log('[this example originally generated TXID: 8284aa3b6f9c71c35ecb1d61d05ae78c8ca1f36940eaa615b50584dfc3d95cb7 on testnet]\n\n\thttp://test.bitcore.io/tx/8284aa3b6f9c71c35ecb1d61d05ae78c8ca1f36940eaa615b50584dfc3d95cb7\n\n');
console.log('[this example originally generated TXID: 2813c5a670d2c9d0527718f9d0ea896c78c3c8fc57b409e67308744fc7a7a98e on testnet]\n\n\thttp://test.bitcore.io/tx/2813c5a670d2c9d0527718f9d0ea896c78c3c8fc57b409e67308744fc7a7a98e');
/*
// To send TX with RPC:
var RpcClient = bitcore.RpcClient;
var config = {
protocol: 'http',
user: 'user',
pass: 'pass',
host: '127.0.0.1',
port: '18332',
};
var rpc = new RpcClient(config);
rpc.sendRawTransaction(txHex, function(err, ret) {
console.log('err', err); //TODO
console.log('ret', ret); //TODO
process.exit(-1);
});
};
*/
};
// This is just for browser & mocha compatibility
if (typeof module !== 'undefined') {
module.exports.run = run;
@ -115,6 +140,3 @@ if (typeof module !== 'undefined') {
} else {
run();
}
////

View File

@ -32,7 +32,7 @@ var run = function() {
//Generate from private Key WIF. Compressed status taken from WIF.
var wk2 = new WalletKey(opts);
wk2.fromObj({priv:'cS62Ej4SobZnpFQYN1PEEBr2KWf5sgRYYnELtumcG6WVCfxno39V'});
wk2.fromObj({priv:'cMpKwGr5oxEacN95WFKNEq6tTcvi11regFwS3muHvGYVxMPJX8JA'});
print(wk2);

View File

@ -65,7 +65,23 @@
"cUkYub4jtFVYymHh38yMMW36nJB4pXG5Pzd5QjResq79kAndkJcg",
"cMyBgowsyrJRufoKWob73rMQB1PBqDdwFt8z4TJ6APN2HkmX1Ttm",
"cN9yZCom6hAZpHtCp8ovE1zFa7RqDf3Cr4W6AwH2tp59Jjh9JcXu"
],
"unspentP2sh": [
{
"address": "2Mwswt6Eih28xH8611fexpqKqJCLJMomveK",
"scriptPubKey": "a91432d272ce8a9b482b363408a0b1dd28123d59c63387",
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
"vout": 1,
"amount": 1,
"confirmations":7
}
],
"keyStringsP2sh": [
"cMpKwGr5oxEacN95WFKNEq6tTcvi11regFwS3muHvGYVxMPJX8JA",
"cVf32m9MR4vxcPwKNJuPepUe8XrHD2z63eCk76d6njRGyCkXpkSM",
"cQ2sVRFX4jQYMLhWyzz6jTQ2xju51P36968ecXnPhRLKLH677eKR",
"cSw7x9ERcmeWCU3yVBT6Nz7b9JiZ5yjUB7JMhBUv9UM7rSaDpwX9",
"cRQBM8qM4ZXJGP1De4D5RtJm7Q6FNWQSMx7YExxzgn2ehjM3haxW"
]
}

View File

@ -11,7 +11,9 @@ var TransactionBuilder = bitcore.TransactionBuilder;
var In;
var Out;
var Script = bitcore.Script;
var WalletKey = bitcore.WalletKey;
var util = bitcore.util;
var networks = bitcore.networks;
var buffertools = require('buffertools');
var testdata = testdata || require('./testdata');
@ -484,16 +486,26 @@ describe('TransactionBuilder', function() {
var k2 = testdata.dataUnspentSign.keyStringsMulti.slice(1,2);
var k3 = testdata.dataUnspentSign.keyStringsMulti.slice(2,3);
b.countInputMultiSig(0).should.equal(0);
var tx = b.build();
b.isFullySigned().should.equal(false);
// This is cumbersome. Before sign, missing is 1. Need to be changed in the future
tx.countInputMissingSignatures(0).should.equal(1);
b.sign(['cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV']);
tx.countInputMissingSignatures(0).should.equal(1);
b.sign(k1);
tx.countInputMissingSignatures(0).should.equal(2);
b.isFullySigned().should.equal(false);
b.countInputMultiSig(0).should.equal(1);
b.sign(k2);
tx.countInputMissingSignatures(0).should.equal(1);
b.isFullySigned().should.equal(false);
b.countInputMultiSig(0).should.equal(2);
b.sign(k3);
tx.countInputMissingSignatures(0).should.equal(0);
b.isFullySigned().should.equal(true);
b.countInputMultiSig(0).should.equal(3);
});
@ -511,20 +523,162 @@ describe('TransactionBuilder', function() {
var k1 = testdata.dataUnspentSign.keyStringsMulti.slice(0,1);
var k23 = testdata.dataUnspentSign.keyStringsMulti.slice(1,3);
var tx = b.build();
b.countInputMultiSig(0).should.equal(0);
tx.countInputMissingSignatures(0).should.equal(1);
b.sign(k1);
b.isFullySigned().should.equal(false);
b.countInputMultiSig(0).should.equal(1);
tx.countInputMissingSignatures(0).should.equal(2);
b.sign(k1);
b.isFullySigned().should.equal(false);
b.countInputMultiSig(0).should.equal(1);
tx.countInputMissingSignatures(0).should.equal(2);
b.sign(k1);
b.isFullySigned().should.equal(false);
b.countInputMultiSig(0).should.equal(1);
tx.countInputMissingSignatures(0).should.equal(2);
b.sign(k23);
b.isFullySigned().should.equal(true);
b.countInputMultiSig(0).should.equal(3);
tx.countInputMissingSignatures(0).should.equal(0);
});
var getInfoForP2sh = function () {
var privs = testdata.dataUnspentSign.keyStringsP2sh;
var pubkeys = [];
privs.forEach(function(p) {
var wk = new WalletKey({network: networks.testnet});
wk.fromObj({priv: p});
pubkeys.push(bitcore.buffertools.toHex(wk.privKey.public));
});
return {
privkeys: privs,
pubkeys: pubkeys,
};
};
var getP2shBuilder = function(setMap) {
var network = 'testnet';
var opts = {
remainderOut: {address: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd'},
};
var data = getInfoForP2sh();
// multisig p2sh
var p2shOpts = {nreq:3, pubkeys:data.pubkeys, amount:0.05};
var info = TransactionBuilder.infoForP2sh(p2shOpts, network);
var outs = outs || [{
address: info.address,
amount: 0.08
}];
var b = new TransactionBuilder(opts)
.setUnspent(testdata.dataUnspentSign.unspentP2sh)
.setOutputs(outs);
if (setMap) {
var hashMap = {};
hashMap[info.address]=info.scriptBufHex;
b.setHashToScriptMap(hashMap);
}
return b;
};
it('should fail to sign a p2sh/multisign tx if none script map was given', function() {
var b = getP2shBuilder();
(function() {b.sign(testdata.dataUnspentSign.keyStringsP2sh);}).should.throw();
});
it('should sign a p2sh/multisign tx', function() {
var b = getP2shBuilder(1);
b.sign(testdata.dataUnspentSign.keyStringsP2sh);
b.isFullySigned().should.equal(true);
var tx = b.build();
tx.ins.length.should.equal(1);
tx.outs.length.should.equal(2);
tx.isComplete().should.equal(true);
});
it('should sign in steps a p2sh/multisign tx', function() {
var b = getP2shBuilder(1);
var k1 = testdata.dataUnspentSign.keyStringsP2sh.slice(0,1);
var k2 = testdata.dataUnspentSign.keyStringsP2sh.slice(1,2);
var k5 = testdata.dataUnspentSign.keyStringsP2sh.slice(4,5);
b.isFullySigned().should.equal(false);
b.sign(k1);
b.isFullySigned().should.equal(false);
var tx = b.build();
tx.ins.length.should.equal(1);
tx.outs.length.should.equal(2);
tx.isComplete().should.equal(false);
// Sign with the same
b.sign(k1);
b.isFullySigned().should.equal(false);
tx.isComplete().should.equal(false);
// Sign with k5
b.sign(k5);
///
b.isFullySigned().should.equal(false);
tx.isComplete().should.equal(false);
// Sign with same
b.sign(k5);
b.isFullySigned().should.equal(false);
tx.isComplete().should.equal(false);
// Sign k2
b.sign(k2);
b.isFullySigned().should.equal(true);
tx.isComplete().should.equal(true);
});
it('should sign in steps a p2sh/p2pubkeyhash tx', function() {
var priv = 'cMpKwGr5oxEacN95WFKNEq6tTcvi11regFwS3muHvGYVxMPJX8JA';
var network = 'testnet';
var opts = {
remainderOut: {address: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd'},
};
// p2hash/ p2sh
var p2shOpts = {address:'mgwqzy6pF5BSc72vxHBFSnnhNEBcV4TJzV', amount:0.05};
var info = TransactionBuilder.infoForP2sh(p2shOpts, network);
//addr: 2NAwCQ1jPYPrSsyBQvfP6AJ6d6SSxnHsZ4e
//hash: de09d4a9c7e53e08043efc74d14490dbcf03b0ba
//
var outs = outs || [{
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
amount: 0.08
}];
//info.scriptBufHex,
var s = TransactionBuilder.scriptForAddress(info.address)
.getBuffer().toString('hex');
var b = new TransactionBuilder(opts)
.setUnspent([{
"address": info.address,
"scriptPubKey": s,
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
"vout": 1,
"amount": 1,
"confirmations":7
}])
.setOutputs(outs);
var hashMap = {};
hashMap[info.address]=info.scriptBufHex;
b.setHashToScriptMap(hashMap);
b.sign([priv]);
b.isFullySigned().should.equal(true);
var tx = b.build();
tx.ins.length.should.equal(1);
tx.outs.length.should.equal(2);
tx.isComplete().should.equal(true);
});
});