TX_MULTISIG support
This commit is contained in:
parent
5dd945f2b7
commit
2af6ab7650
|
@ -553,15 +553,28 @@ Transaction.prototype.getSize = function getHash() {
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.prototype.isComplete = function() {
|
Transaction.prototype.isComplete = function() {
|
||||||
var l = this.ins.length;
|
|
||||||
|
|
||||||
var ret = true;
|
var ret = true;
|
||||||
|
var l = this.ins.length;
|
||||||
|
|
||||||
for (var i = 0; i < l; i++) {
|
for (var i = 0; i < l; i++) {
|
||||||
if (buffertools.compare(this.ins[i].s, util.EMPTY_BUFFER) === 0) {
|
var script = new Script(this.ins[i].s);
|
||||||
ret = false;
|
// Multisig?
|
||||||
break;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (buffertools.compare(this.ins[i].s, util.EMPTY_BUFFER) === 0) {
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ 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);
|
||||||
|
|
||||||
function TransactionBuilder(opts) {
|
function TransactionBuilder(opts) {
|
||||||
var opts = opts || {};
|
opts = opts || {};
|
||||||
this.txobj = {};
|
this.txobj = {};
|
||||||
this.txobj.version = 1;
|
this.txobj.version = 1;
|
||||||
this.txobj.lock_time = opts.lockTime || 0;
|
this.txobj.lock_time = opts.lockTime || 0;
|
||||||
|
@ -144,18 +144,29 @@ 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 s = this.selectedUtxos[i];
|
var utxo = this.selectedUtxos[i];
|
||||||
|
|
||||||
|
var scriptBuf = new Buffer(utxo.scriptPubKey, 'hex');
|
||||||
|
var scriptPubKey = new Script(scriptBuf);
|
||||||
|
var scriptType = scriptPubKey.classify();
|
||||||
|
|
||||||
|
if (scriptType === Script.TX_UNKNOWN)
|
||||||
|
throw new Error('unkown output type at:' + i +
|
||||||
|
' Type:' + scriptPubKey.getRawOutType());
|
||||||
|
|
||||||
inputMap.push({
|
inputMap.push({
|
||||||
address: s.address,
|
address: utxo.address, //TODO que pasa en multisig normal?
|
||||||
scriptPubKey: s.scriptPubKey
|
scriptPubKeyHex: utxo.scriptPubKey,
|
||||||
|
scriptPubKey: scriptPubKey,
|
||||||
|
scriptType: scriptType,
|
||||||
|
i: i,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.inputMap = inputMap;
|
this.inputMap = inputMap;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionBuilder.prototype.getSelectedUnspent = function(neededAmountSat) {
|
TransactionBuilder.prototype.getSelectedUnspent = function() {
|
||||||
return this.selectedUtxos;
|
return this.selectedUtxos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -360,14 +371,6 @@ TransactionBuilder._mapKeys = function(keys) {
|
||||||
return walletKeyMap;
|
return walletKeyMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionBuilder._checkSupportedScriptType = function (s) {
|
|
||||||
if (s.classify() !== Script.TX_PUBKEYHASH) {
|
|
||||||
throw new Error('scriptSig type:' + s.getRawOutType() +
|
|
||||||
' not supported yet');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
TransactionBuilder._signHashAndVerify = function(wk, txSigHash) {
|
TransactionBuilder._signHashAndVerify = function(wk, txSigHash) {
|
||||||
var triesLeft = 10, sigRaw;
|
var triesLeft = 10, sigRaw;
|
||||||
|
|
||||||
|
@ -387,44 +390,208 @@ TransactionBuilder.prototype._checkTx = function() {
|
||||||
throw new Error('tx is not defined');
|
throw new Error('tx is not defined');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TransactionBuilder.prototype._signPubKey = function(walletKeyMap, input, txSigHash) {
|
||||||
|
if (this.tx.ins[input.i].s.length > 0) return {};
|
||||||
|
|
||||||
|
var wk = walletKeyMap[input.address];
|
||||||
|
if (!wk) return;
|
||||||
|
|
||||||
|
var sigRaw = TransactionBuilder._signHashAndVerify(wk, txSigHash);
|
||||||
|
var sigType = new Buffer(1);
|
||||||
|
sigType[0] = this.signhash;
|
||||||
|
var sig = Buffer.concat([sigRaw, sigType]);
|
||||||
|
|
||||||
|
var scriptSig = new Script();
|
||||||
|
scriptSig.chunks.push(sig);
|
||||||
|
scriptSig.updateBuffer();
|
||||||
|
return {isFullySigned: 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];
|
||||||
|
if (!wk) return;
|
||||||
|
|
||||||
|
var sigRaw = TransactionBuilder._signHashAndVerify(wk, txSigHash);
|
||||||
|
var sigType = new Buffer(1);
|
||||||
|
sigType[0] = this.signhash;
|
||||||
|
var sig = Buffer.concat([sigRaw, sigType]);
|
||||||
|
|
||||||
|
var scriptSig = new Script();
|
||||||
|
scriptSig.chunks.push(sig);
|
||||||
|
scriptSig.chunks.push(wk.privKey.public);
|
||||||
|
scriptSig.updateBuffer();
|
||||||
|
return {isFullySigned: 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]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
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) {
|
||||||
|
var ret=0;
|
||||||
|
for(var i=1; i<=nreq; i++) {
|
||||||
|
var chunk = scriptSig.chunks[i];
|
||||||
|
if (chunk ===0 || chunk.length === 0) continue;
|
||||||
|
|
||||||
|
var sigRaw = new Buffer(chunk.slice(0,chunk.length-1));
|
||||||
|
if(wk.privKey.verifySignatureSync(txSigHash, sigRaw) === true) {
|
||||||
|
ret=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
TransactionBuilder.prototype._chunkIsEmpty = function(chunk) {
|
||||||
|
return chunk === 0 || // when serializing and back, EMPTY_BUFFER becomes 0
|
||||||
|
buffertools.compare(chunk, util.EMPTY_BUFFER) === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
TransactionBuilder.prototype._updateMultiSig = function(wk, scriptSig, txSigHash, nreq) {
|
||||||
|
var wasUpdated = this._initMultiSig(scriptSig, nreq);
|
||||||
|
|
||||||
|
if (this._isSignedWithKey(wk,scriptSig, txSigHash, nreq))
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return wasUpdated ? scriptSig : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
l = pubkeys.length,
|
||||||
|
originalScriptBuf = this.tx.ins[input.i].s;
|
||||||
|
|
||||||
|
var scriptSig = new Script (originalScriptBuf);
|
||||||
|
|
||||||
|
for(var j=0; j<l && this._countMultiSig(scriptSig)<nreq; j++) {
|
||||||
|
|
||||||
|
var pubKeyHash = util.sha256ripe160(pubkeys[j]);
|
||||||
|
var wk = this._multiFindKey(walletKeyMap, pubKeyHash);
|
||||||
|
if (!wk) continue;
|
||||||
|
|
||||||
|
var newScriptSig = this._updateMultiSig(wk, scriptSig, txSigHash, nreq);
|
||||||
|
if (newScriptSig)
|
||||||
|
scriptSig = newScriptSig;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isFullySigned: this._countMultiSig(scriptSig) === nreq,
|
||||||
|
script: scriptSig.getBuffer(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var fnToSign = {};
|
||||||
|
fnToSign[Script.TX_PUBKEYHASH] = TransactionBuilder.prototype._signPubKeyHash;
|
||||||
|
fnToSign[Script.TX_PUBKEY] = TransactionBuilder.prototype._signPubKey;
|
||||||
|
fnToSign[Script.TX_MULTISIG] = TransactionBuilder.prototype._signMultiSig;
|
||||||
|
fnToSign[Script.TX_SCRIPTHASH] = TransactionBuilder.prototype._signScriptHash;
|
||||||
|
//if (!this.hashToScriptMap) throw new Error('hashToScriptMap not set');
|
||||||
|
|
||||||
TransactionBuilder.prototype.sign = function(keys) {
|
TransactionBuilder.prototype.sign = function(keys) {
|
||||||
this._checkTx();
|
this._checkTx();
|
||||||
|
|
||||||
var tx = this.tx,
|
var tx = this.tx,
|
||||||
ins = tx.ins,
|
ins = tx.ins,
|
||||||
l = ins.length;
|
l = ins.length,
|
||||||
|
walletKeyMap = TransactionBuilder._mapKeys(keys);
|
||||||
|
|
||||||
var walletKeyMap = TransactionBuilder._mapKeys(keys);
|
|
||||||
|
|
||||||
for (var i = 0; i < l; i++) {
|
for (var i = 0; i < l; i++) {
|
||||||
var im = this.inputMap[i];
|
var input = this.inputMap[i];
|
||||||
if (typeof im === 'undefined') continue;
|
|
||||||
var wk = walletKeyMap[im.address];
|
|
||||||
if (!wk) continue;
|
|
||||||
|
|
||||||
var scriptBuf = new Buffer(im.scriptPubKey, 'hex');
|
var txSigHash = this.tx.hashForSignature(
|
||||||
|
input.scriptPubKey, i, this.signhash);
|
||||||
|
|
||||||
//TODO: support p2sh
|
|
||||||
var s = new Script(scriptBuf);
|
|
||||||
TransactionBuilder._checkSupportedScriptType(s);
|
|
||||||
|
|
||||||
var txSigHash = this.tx.hashForSignature(s, i, this.signhash);
|
var ret = fnToSign[input.scriptType].call(this, walletKeyMap, input, txSigHash);
|
||||||
var sigRaw = TransactionBuilder._signHashAndVerify(wk, txSigHash);
|
if (ret && ret.script) {
|
||||||
var sigType = new Buffer(1);
|
tx.ins[i].s = ret.script; //esto no aqui TODO
|
||||||
sigType[0] = this.signhash;
|
if (ret.isFullySigned) this.inputsSigned++;
|
||||||
var sig = Buffer.concat([sigRaw, sigType]);
|
}
|
||||||
|
|
||||||
var scriptSig = new Script();
|
|
||||||
scriptSig.chunks.push(sig);
|
|
||||||
scriptSig.chunks.push(wk.privKey.public);
|
|
||||||
scriptSig.updateBuffer();
|
|
||||||
tx.ins[i].s = scriptSig.getBuffer();
|
|
||||||
this.inputsSigned++;
|
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// [addr -> script]
|
||||||
|
TransactionBuilder.prototype.setHashToScriptMap = function(hashToScriptMap) {
|
||||||
|
this.hashToScriptMap= hashToScriptMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
TransactionBuilder.prototype.isFullySigned = function() {
|
TransactionBuilder.prototype.isFullySigned = function() {
|
||||||
return this.inputsSigned === this.tx.ins.length;
|
return this.inputsSigned === this.tx.ins.length;
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,7 +9,7 @@ var run = function() {
|
||||||
var buffertools = bitcore.buffertools;
|
var buffertools = bitcore.buffertools;
|
||||||
var Address = bitcore.Address;
|
var Address = bitcore.Address;
|
||||||
var util = bitcore.util;
|
var util = bitcore.util;
|
||||||
var opts = {network: networks.livenet};
|
var opts = {network: networks.testnet};
|
||||||
|
|
||||||
var p = console.log;
|
var p = console.log;
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
{
|
{
|
||||||
"address": "mqSjTad2TKbPcKQ3Jq4kgCkKatyN44UMgZ",
|
"address": "mqSjTad2TKbPcKQ3Jq4kgCkKatyN44UMgZ",
|
||||||
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2",
|
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2",
|
||||||
"scriptPubKey": "76a9146ce4e1163eb18939b1440c42844d5f0261c0338288ad",
|
"scriptPubKey": "76a9146ce4e1163eb18939b1440c42844d5f0261c0338288ac",
|
||||||
"vout": 0,
|
"vout": 0,
|
||||||
"confirmations": 1,
|
"confirmations": 1,
|
||||||
"amount": 0.1
|
"amount": 0.1
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
{
|
{
|
||||||
"address": "mqSjTad2TKbPcKQ3Jq4kgCkKatyN44UMgZ",
|
"address": "mqSjTad2TKbPcKQ3Jq4kgCkKatyN44UMgZ",
|
||||||
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc3",
|
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc3",
|
||||||
"scriptPubKey": "76a9146ce4e1163eb18939b1440c42844d5f0261c0338288ae",
|
"scriptPubKey": "76a9146ce4e1163eb18939b1440c42844d5f0261c0338288ac",
|
||||||
"vout": 3,
|
"vout": 3,
|
||||||
"confirmations": 0,
|
"confirmations": 0,
|
||||||
"amount": 1
|
"amount": 1
|
||||||
|
|
|
@ -29,6 +29,43 @@
|
||||||
"cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV",
|
"cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV",
|
||||||
"cPa87VgwZfowGZYaEenoQeJgRfKW6PhZ1R65EHTkN1K19cSvc92G",
|
"cPa87VgwZfowGZYaEenoQeJgRfKW6PhZ1R65EHTkN1K19cSvc92G",
|
||||||
"cPQ9DSbBRLva9av5nqeF5AGrh3dsdW8p2E5jS4P8bDWZAoQTeeKB"
|
"cPQ9DSbBRLva9av5nqeF5AGrh3dsdW8p2E5jS4P8bDWZAoQTeeKB"
|
||||||
|
],
|
||||||
|
"unspentPubKey": [
|
||||||
|
{
|
||||||
|
"address": "mqqnn93xN81eZTLqj7Wk2cacBBTR8agFZ5",
|
||||||
|
"scriptPubKey": "2102aa869ff719f23d9959dca340cbf3b72770294c64005e53e0429948aa6e9701d1ac",
|
||||||
|
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
|
||||||
|
"vout": 1,
|
||||||
|
"amount": 1,
|
||||||
|
"confirmations":7
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"keyStringsPubKey": [
|
||||||
|
"cTSvhK2b3XxJezmDjVN5x1KTCtui4NaLhvb78nvprpVAiqHgQvMm"
|
||||||
|
],
|
||||||
|
"unspentMulti": [
|
||||||
|
{
|
||||||
|
"address": [
|
||||||
|
"n4JAZc4cJimQbky5wxZUEDeAFZtGaZrjWK",
|
||||||
|
"msge5muNmBSRDn5nsaRcHCU6dg2zimA8wQ",
|
||||||
|
"mvz9MjocpyXdgXqRcZYazsdE8iThdvjdhk",
|
||||||
|
"miQGZ2gybQe7UvUQDBYsgcctUteij5pTpm",
|
||||||
|
"mu9kmhGrzREKsWaXUEUrsRLLMG4UMPy1LF"
|
||||||
|
],
|
||||||
|
"scriptPubKey": "532103bf025eb410407aec5a67c975ce222e363bb88c69bb1acce45d20d85602df2ec52103d76dd6d99127f4b733e772f0c0a09c573ac7e4d69b8bf50272292da2e093de2c2103dd9acd8dd1816c825d6b0739339c171ae2cb10efb53699680537865b07086e9b2102371cabbaf466c3a536034b4bda64ad515807bffd87488f44f93c2373d4d189c9210264cd444358f8d57f8637a7309f9736806f4883aebc4fe7da4bad1e4b37f2d12c55ae",
|
||||||
|
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
|
||||||
|
"vout": 1,
|
||||||
|
"amount": 1,
|
||||||
|
"confirmations":7
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"keyStringsMulti": [
|
||||||
|
"cP6JBHuQf7yqeqtdKRd22ibF3VehDv7G6BdzxSNABgrv3jFJUGoN",
|
||||||
|
"cQfRwF7XLSM5xGUpF8PZvob2MZyULvZPA2j5cat2RKDJrja7FtCZ",
|
||||||
|
"cUkYub4jtFVYymHh38yMMW36nJB4pXG5Pzd5QjResq79kAndkJcg",
|
||||||
|
"cMyBgowsyrJRufoKWob73rMQB1PBqDdwFt8z4TJ6APN2HkmX1Ttm",
|
||||||
|
"cN9yZCom6hAZpHtCp8ovE1zFa7RqDf3Cr4W6AwH2tp59Jjh9JcXu"
|
||||||
]
|
]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -220,7 +220,6 @@ describe('TransactionBuilder', function() {
|
||||||
amount: 0.08
|
amount: 0.08
|
||||||
}];
|
}];
|
||||||
|
|
||||||
//console.log('[test.TransactionBuilder.js.216:outs:]',outs, outs.length); //TODO
|
|
||||||
return new TransactionBuilder(opts)
|
return new TransactionBuilder(opts)
|
||||||
.setUnspent(testdata.dataUnspentSign.unspent)
|
.setUnspent(testdata.dataUnspentSign.unspent)
|
||||||
.setOutputs(outs);
|
.setOutputs(outs);
|
||||||
|
@ -395,4 +394,107 @@ describe('TransactionBuilder', function() {
|
||||||
util.valueToBigInt(tx.outs[N].v).cmp(970000).should.equal(0);
|
util.valueToBigInt(tx.outs[N].v).cmp(970000).should.equal(0);
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should sign a p2pubkey tx', function() {
|
||||||
|
var opts = {
|
||||||
|
remainderAddress: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd',
|
||||||
|
};
|
||||||
|
var outs = outs || [{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 0.08
|
||||||
|
}];
|
||||||
|
|
||||||
|
var b = new TransactionBuilder(opts)
|
||||||
|
.setUnspent(testdata.dataUnspentSign.unspentPubKey)
|
||||||
|
.setOutputs(outs)
|
||||||
|
.sign(testdata.dataUnspentSign.keyStringsPubKey);
|
||||||
|
|
||||||
|
b.isFullySigned().should.equal(true);
|
||||||
|
var tx = b.build();
|
||||||
|
tx.isComplete().should.equal(true);
|
||||||
|
tx.ins.length.should.equal(1);
|
||||||
|
tx.outs.length.should.equal(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should sign a multisig tx', function() {
|
||||||
|
var opts = {
|
||||||
|
remainderAddress: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd',
|
||||||
|
};
|
||||||
|
var outs = outs || [{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 0.08
|
||||||
|
}];
|
||||||
|
var b = new TransactionBuilder(opts)
|
||||||
|
.setUnspent(testdata.dataUnspentSign.unspentMulti)
|
||||||
|
.setOutputs(outs);
|
||||||
|
|
||||||
|
b.sign(testdata.dataUnspentSign.keyStringsMulti);
|
||||||
|
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 a multisig tx in steps (3-5)', function() {
|
||||||
|
var opts = {
|
||||||
|
remainderAddress: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd',
|
||||||
|
};
|
||||||
|
var outs = outs || [{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 0.08
|
||||||
|
}];
|
||||||
|
var b = new TransactionBuilder(opts)
|
||||||
|
.setUnspent(testdata.dataUnspentSign.unspentMulti)
|
||||||
|
.setOutputs(outs);
|
||||||
|
|
||||||
|
var k1 = testdata.dataUnspentSign.keyStringsMulti.slice(0,1);
|
||||||
|
var k2 = testdata.dataUnspentSign.keyStringsMulti.slice(1,2);
|
||||||
|
var k3 = testdata.dataUnspentSign.keyStringsMulti.slice(2,3);
|
||||||
|
|
||||||
|
b.sign(k1);
|
||||||
|
b.isFullySigned().should.equal(false);
|
||||||
|
b.sign(k2);
|
||||||
|
b.isFullySigned().should.equal(false);
|
||||||
|
b.sign(k3);
|
||||||
|
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 count multisig signs (3-5)', function() {
|
||||||
|
var opts = {
|
||||||
|
remainderAddress: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd',
|
||||||
|
};
|
||||||
|
var outs = outs || [{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 0.08
|
||||||
|
}];
|
||||||
|
var b = new TransactionBuilder(opts)
|
||||||
|
.setUnspent(testdata.dataUnspentSign.unspentMulti)
|
||||||
|
.setOutputs(outs);
|
||||||
|
|
||||||
|
var k1 = testdata.dataUnspentSign.keyStringsMulti.slice(0,1);
|
||||||
|
var k2 = testdata.dataUnspentSign.keyStringsMulti.slice(1,2);
|
||||||
|
var k3 = testdata.dataUnspentSign.keyStringsMulti.slice(2,3);
|
||||||
|
|
||||||
|
b.countInputMultiSig(0).should.equal(0);
|
||||||
|
b.sign(k1);
|
||||||
|
b.isFullySigned().should.equal(false);
|
||||||
|
b.countInputMultiSig(0).should.equal(1);
|
||||||
|
b.sign(k2);
|
||||||
|
b.isFullySigned().should.equal(false);
|
||||||
|
b.countInputMultiSig(0).should.equal(2);
|
||||||
|
b.sign(k3);
|
||||||
|
b.isFullySigned().should.equal(true);
|
||||||
|
b.countInputMultiSig(0).should.equal(3);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue