PayToScriptHash support (WIP)
This commit is contained in:
parent
4fe8dffe4a
commit
4edab2429a
|
@ -154,7 +154,7 @@ TransactionBuilder._scriptForPubkeys = function(out) {
|
||||||
TransactionBuilder._scriptForOut = function(out) {
|
TransactionBuilder._scriptForOut = function(out) {
|
||||||
var ret;
|
var ret;
|
||||||
if (out.address)
|
if (out.address)
|
||||||
ret = this._scriptForAddress(out.address)
|
ret = this._scriptForAddress(out.address);
|
||||||
else if (out.pubkeys || out.nreq || out.nreq > 1)
|
else if (out.pubkeys || out.nreq || out.nreq > 1)
|
||||||
ret = this._scriptForPubkeys(out);
|
ret = this._scriptForPubkeys(out);
|
||||||
else
|
else
|
||||||
|
@ -163,6 +163,24 @@ TransactionBuilder._scriptForOut = function(out) {
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TransactionBuilder.infoForP2sh = function(opts, networkName) {
|
||||||
|
var script = this._scriptForOut(opts);
|
||||||
|
var hash = util.sha256ripe160(script.getBuffer());
|
||||||
|
|
||||||
|
var version = networkName === 'testnet' ?
|
||||||
|
networks.testnet.addressScript : networks.livenet.addressScript;
|
||||||
|
|
||||||
|
var addr = new Address(version, hash);
|
||||||
|
var addrStr = addr.as('base58');
|
||||||
|
return {
|
||||||
|
script: script,
|
||||||
|
scriptBufHex: script.getBuffer().toString('hex'),
|
||||||
|
hash: hash,
|
||||||
|
address: addrStr,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
TransactionBuilder.prototype.setUnspent = function(utxos) {
|
TransactionBuilder.prototype.setUnspent = function(utxos) {
|
||||||
this.utxos = utxos;
|
this.utxos = utxos;
|
||||||
return this;
|
return this;
|
||||||
|
@ -173,8 +191,7 @@ TransactionBuilder.prototype._setInputMap = function() {
|
||||||
|
|
||||||
var l = this.selectedUtxos.length;
|
var l = this.selectedUtxos.length;
|
||||||
for (var i = 0; i < l; i++) {
|
for (var i = 0; i < l; i++) {
|
||||||
var utxo = this.selectedUtxos[i];
|
var utxo = this.selectedUtxos[i];
|
||||||
|
|
||||||
var scriptBuf = new Buffer(utxo.scriptPubKey, 'hex');
|
var scriptBuf = new Buffer(utxo.scriptPubKey, 'hex');
|
||||||
var scriptPubKey = new Script(scriptBuf);
|
var scriptPubKey = new Script(scriptBuf);
|
||||||
var scriptType = scriptPubKey.classify();
|
var scriptType = scriptPubKey.classify();
|
||||||
|
@ -582,16 +599,61 @@ TransactionBuilder.prototype._signMultiSig = function(walletKeyMap, input, txSig
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var fnToSign = {};
|
||||||
|
|
||||||
|
|
||||||
TransactionBuilder.prototype._signScriptHash = function(walletKeyMap, input, txSigHash) {
|
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');
|
||||||
|
|
||||||
|
var scriptHex = this.hashToScriptMap[input.address];
|
||||||
|
if (!scriptHex) return;
|
||||||
|
|
||||||
throw new Error('TX_SCRIPTHASH not supported yet');
|
var script = new Script(new Buffer(scriptHex,'hex'));
|
||||||
|
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());
|
||||||
|
|
||||||
|
var newInput = {
|
||||||
|
address: 'TODO', // if p2pkubkeyhash -> get the address
|
||||||
|
i: input.i,
|
||||||
|
scriptPubKey: script,
|
||||||
|
scriptPubKeyHex: scriptPubKeyHex ,
|
||||||
|
scriptType: scriptType,
|
||||||
|
};
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var fnToSign = {};
|
|
||||||
fnToSign[Script.TX_PUBKEYHASH] = TransactionBuilder.prototype._signPubKeyHash;
|
fnToSign[Script.TX_PUBKEYHASH] = TransactionBuilder.prototype._signPubKeyHash;
|
||||||
fnToSign[Script.TX_PUBKEY] = TransactionBuilder.prototype._signPubKey;
|
fnToSign[Script.TX_PUBKEY] = TransactionBuilder.prototype._signPubKey;
|
||||||
fnToSign[Script.TX_MULTISIG] = TransactionBuilder.prototype._signMultiSig;
|
fnToSign[Script.TX_MULTISIG] = TransactionBuilder.prototype._signMultiSig;
|
||||||
|
@ -605,13 +667,13 @@ TransactionBuilder.prototype.sign = function(keys) {
|
||||||
walletKeyMap = TransactionBuilder._mapKeys(keys);
|
walletKeyMap = TransactionBuilder._mapKeys(keys);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (var i = 0; i < l; i++) {
|
for (var i = 0; i < l; i++) {
|
||||||
var input = this.inputMap[i];
|
var input = this.inputMap[i];
|
||||||
|
|
||||||
var txSigHash = this.tx.hashForSignature(
|
var txSigHash = this.tx.hashForSignature(
|
||||||
input.scriptPubKey, i, this.signhash);
|
input.scriptPubKey, i, this.signhash);
|
||||||
|
|
||||||
|
|
||||||
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; //esto no aqui TODO
|
tx.ins[i].s = ret.script; //esto no aqui TODO
|
||||||
|
@ -624,6 +686,8 @@ TransactionBuilder.prototype.sign = function(keys) {
|
||||||
// [addr -> script]
|
// [addr -> script]
|
||||||
TransactionBuilder.prototype.setHashToScriptMap = function(hashToScriptMap) {
|
TransactionBuilder.prototype.setHashToScriptMap = function(hashToScriptMap) {
|
||||||
this.hashToScriptMap= hashToScriptMap;
|
this.hashToScriptMap= hashToScriptMap;
|
||||||
|
|
||||||
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
var run = function() {
|
||||||
|
bitcore = typeof (bitcore) === 'undefined' ? require('../bitcore') : bitcore;
|
||||||
|
var networks = require('../networks');
|
||||||
|
var WalletKey = bitcore.WalletKey;
|
||||||
|
var Script = bitcore.Script;
|
||||||
|
var Builder = bitcore.TransactionBuilder;
|
||||||
|
var opts = {network: networks.testnet};
|
||||||
|
|
||||||
|
console.log('## Network: ' + opts.network.name);
|
||||||
|
|
||||||
|
var input = {};
|
||||||
|
input.addr = "n2hoFVbPrYQf7RJwiRy1tkbuPPqyhAEfbp";
|
||||||
|
input.priv = "cS62Ej4SobZnpFQYN1PEEBr2KWf5sgRYYnELtumcG6WVCfxno39V";
|
||||||
|
|
||||||
|
// Complete with the corresponding UTXO you want to use
|
||||||
|
var utxos = [
|
||||||
|
{
|
||||||
|
address: "n2hoFVbPrYQf7RJwiRy1tkbuPPqyhAEfbp",
|
||||||
|
txid: "ff5c8b4912f6d056f0cf8431ec27032a73df22c167726267dd4cc0d7817a1e7d",
|
||||||
|
vout: 1,
|
||||||
|
ts: 1396290442,
|
||||||
|
scriptPubKey: "76a914e867aad8bd361f57c50adc37a0c018692b5b0c9a88ac",
|
||||||
|
amount: 0.5799,
|
||||||
|
confirmations: 7
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
var privs = [
|
||||||
|
"cP6JBHuQf7yqeqtdKRd22ibF3VehDv7G6BdzxSNABgrv3jFJUGoN",
|
||||||
|
"cQfRwF7XLSM5xGUpF8PZvob2MZyULvZPA2j5cat2RKDJrja7FtCZ",
|
||||||
|
"cUkYub4jtFVYymHh38yMMW36nJB4pXG5Pzd5QjResq79kAndkJcg",
|
||||||
|
"cMyBgowsyrJRufoKWob73rMQB1PBqDdwFt8z4TJ6APN2HkmX1Ttm",
|
||||||
|
"cN9yZCom6hAZpHtCp8ovE1zFa7RqDf3Cr4W6AwH2tp59Jjh9JcXu",
|
||||||
|
];
|
||||||
|
|
||||||
|
var pubkeys = []
|
||||||
|
privs.forEach(function(p) {
|
||||||
|
var wk = new WalletKey(opts);
|
||||||
|
wk.fromObj({priv: p});
|
||||||
|
pubkeys.push(bitcore.buffertools.toHex(wk.privKey.public));
|
||||||
|
});
|
||||||
|
|
||||||
|
// multisig p2sh
|
||||||
|
var opts = {nreq:3, pubkeys:pubkeys, amount:0.05};
|
||||||
|
|
||||||
|
// p2scriphash p2sh
|
||||||
|
//var opts = [{address: an_address, amount:0.05}];
|
||||||
|
|
||||||
|
var info = Builder.infoForP2sh(opts, 'testnet');
|
||||||
|
var p2shScript = info.scriptBufHex;
|
||||||
|
var p2shAddress = info.address;
|
||||||
|
var outs = [{address:p2shAddress, amount:0.05}];
|
||||||
|
var tx = new Builder(opts)
|
||||||
|
.setUnspent(utxos)
|
||||||
|
.setOutputs(outs)
|
||||||
|
.sign([input.priv])
|
||||||
|
.build();
|
||||||
|
var txHex = tx.serialize().toString('hex');
|
||||||
|
console.log('1) SEND TO P2SH TX: ', txHex);
|
||||||
|
console.log('[this example originally generated TXID: ba20653648a896ae95005b8f52847935a7313da06cd7295bb2cfc8b5c1b36c71 on testnet]\n\n\thttp://test.bitcore.io/tx/ba20653648a896ae95005b8f52847935a7313da06cd7295bb2cfc8b5c1b36c71\n\n');
|
||||||
|
|
||||||
|
//save scriptPubKey
|
||||||
|
var scriptPubKey = tx.outs[0].s.toString('hex');
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* REDDEEM TX
|
||||||
|
*/
|
||||||
|
var utxos2 = [
|
||||||
|
{
|
||||||
|
address: p2shAddress,
|
||||||
|
txid: "ba20653648a896ae95005b8f52847935a7313da06cd7295bb2cfc8b5c1b36c71",
|
||||||
|
vout: 0,
|
||||||
|
ts: 1396288753,
|
||||||
|
scriptPubKey: scriptPubKey,
|
||||||
|
amount: 0.05,
|
||||||
|
confirmations: 2
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
outs = [{address:input.addr, amount:0.04}];
|
||||||
|
|
||||||
|
var hashMap = {};
|
||||||
|
hashMap[p2shAddress]=p2shScript;
|
||||||
|
|
||||||
|
var b = new Builder(opts)
|
||||||
|
.setUnspent(utxos2)
|
||||||
|
.setHashToScriptMap(hashMap)
|
||||||
|
.setOutputs(outs)
|
||||||
|
.sign(privs);
|
||||||
|
|
||||||
|
|
||||||
|
tx= b.build();
|
||||||
|
|
||||||
|
|
||||||
|
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: 2813c5a670d2c9d0527718f9d0ea896c78c3c8fc57b409e67308744fc7a7a98e on testnet]\n\n\thttp://test.bitcore.io/tx/2813c5a670d2c9d0527718f9d0ea896c78c3c8fc57b409e67308744fc7a7a98e');
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is just for browser & mocha compatibility
|
||||||
|
if (typeof module !== 'undefined') {
|
||||||
|
module.exports.run = run;
|
||||||
|
if (require.main === module) {
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
|
////
|
||||||
|
|
Loading…
Reference in New Issue