Merge branch 'feature/TransactionBuilder-Multisig-support'
This commit is contained in:
commit
42522e03b9
18
Script.js
18
Script.js
|
@ -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) {
|
||||
|
|
|
@ -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){
|
||||
if (!this.isInputComplete(i)){
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (buffertools.compare(this.ins[i].s, util.EMPTY_BUFFER) === 0) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
|
|
@ -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,7 @@ TransactionBuilder.prototype._chunkIsEmpty = function(chunk) {
|
|||
buffertools.compare(chunk, util.EMPTY_BUFFER) === 0;
|
||||
};
|
||||
|
||||
|
||||
TransactionBuilder.prototype._updateMultiSig = function(wk, scriptSig, txSigHash, nreq) {
|
||||
var wasUpdated = this._initMultiSig(scriptSig, nreq);
|
||||
|
||||
|
@ -540,40 +570,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 +577,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 scriptBuf = new Buffer(scriptHex,'hex');
|
||||
var script = new Script(scriptBuf);
|
||||
var scriptType = script.classify();
|
||||
var scriptPubKeyHex = script.getBuffer().toString('hex');
|
||||
|
||||
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 (ret && ret.script && ret.signaturesAdded) {
|
||||
ret.script = this._addScript(ret.script, scriptBuf);
|
||||
}
|
||||
|
||||
if (scriptType == Script.TX_MULTISIG && scriptSig.finishedMultiSig())
|
||||
{
|
||||
scriptSig.removePlaceHolders();
|
||||
scriptSig.prependOp0();
|
||||
ret.script = scriptSig.getBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
@ -676,17 +692,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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
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();
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue