Merge branch 'master' of github.com:bitpay/bitcore into clean
This commit is contained in:
commit
433e40f390
|
@ -10,3 +10,4 @@ node_modules/
|
||||||
README.html
|
README.html
|
||||||
tags
|
tags
|
||||||
coverage
|
coverage
|
||||||
|
.DS_Store
|
||||||
|
|
|
@ -94,12 +94,7 @@ var FEE_PER_1000B_SAT = parseInt(0.0001 * util.COIN);
|
||||||
|
|
||||||
function TransactionBuilder(opts) {
|
function TransactionBuilder(opts) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
this.txobj = {};
|
this.lockTime = opts.lockTime || 0;
|
||||||
this.txobj.version = 1;
|
|
||||||
this.txobj.lock_time = opts.lockTime || 0;
|
|
||||||
this.txobj.ins = [];
|
|
||||||
this.txobj.outs = [];
|
|
||||||
|
|
||||||
this.spendUnconfirmed = opts.spendUnconfirmed || false;
|
this.spendUnconfirmed = opts.spendUnconfirmed || false;
|
||||||
|
|
||||||
if (opts.fee || opts.feeSat) {
|
if (opts.fee || opts.feeSat) {
|
||||||
|
@ -265,12 +260,12 @@ TransactionBuilder.prototype._selectUnspent = function(neededAmountSat) {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionBuilder.prototype._setInputs = function() {
|
TransactionBuilder.prototype._setInputs = function(txobj) {
|
||||||
var ins = this.selectedUtxos;
|
var ins = this.selectedUtxos;
|
||||||
var l = ins.length;
|
var l = ins.length;
|
||||||
var valueInSat = bignum(0);
|
var valueInSat = bignum(0);
|
||||||
|
|
||||||
this.txobj.ins=[];
|
txobj.ins=[];
|
||||||
for (var i = 0; i < l; i++) {
|
for (var i = 0; i < l; i++) {
|
||||||
valueInSat = valueInSat.add(util.parseValue(ins[i].amount));
|
valueInSat = valueInSat.add(util.parseValue(ins[i].amount));
|
||||||
|
|
||||||
|
@ -286,7 +281,7 @@ TransactionBuilder.prototype._setInputs = function() {
|
||||||
voutBuf.writeUInt32LE(vout, 0);
|
voutBuf.writeUInt32LE(vout, 0);
|
||||||
|
|
||||||
txin.o = Buffer.concat([hashReversed, voutBuf]);
|
txin.o = Buffer.concat([hashReversed, voutBuf]);
|
||||||
this.txobj.ins.push(txin);
|
txobj.ins.push(txin);
|
||||||
}
|
}
|
||||||
this.valueInSat = valueInSat;
|
this.valueInSat = valueInSat;
|
||||||
return this;
|
return this;
|
||||||
|
@ -309,7 +304,7 @@ TransactionBuilder.prototype._setFee = function(feeSat) {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionBuilder.prototype._setRemainder = function(remainderIndex) {
|
TransactionBuilder.prototype._setRemainder = function(txobj, remainderIndex) {
|
||||||
|
|
||||||
if ( typeof this.valueInSat === 'undefined' ||
|
if ( typeof this.valueInSat === 'undefined' ||
|
||||||
typeof this.valueOutSat === 'undefined')
|
typeof this.valueOutSat === 'undefined')
|
||||||
|
@ -317,12 +312,12 @@ TransactionBuilder.prototype._setRemainder = function(remainderIndex) {
|
||||||
|
|
||||||
// add remainder (without modifying outs[])
|
// add remainder (without modifying outs[])
|
||||||
var remainderSat = this.valueInSat.sub(this.valueOutSat).sub(this.feeSat);
|
var remainderSat = this.valueInSat.sub(this.valueOutSat).sub(this.feeSat);
|
||||||
var l =this.txobj.outs.length;
|
var l =txobj.outs.length;
|
||||||
this.remainderSat = bignum(0);
|
this.remainderSat = bignum(0);
|
||||||
|
|
||||||
//remove old remainder?
|
//remove old remainder?
|
||||||
if (l > remainderIndex) {
|
if (l > remainderIndex) {
|
||||||
this.txobj.outs.pop();
|
txobj.outs.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remainderSat.cmp(0) > 0) {
|
if (remainderSat.cmp(0) > 0) {
|
||||||
|
@ -333,18 +328,17 @@ TransactionBuilder.prototype._setRemainder = function(remainderIndex) {
|
||||||
v: value,
|
v: value,
|
||||||
s: script.getBuffer(),
|
s: script.getBuffer(),
|
||||||
};
|
};
|
||||||
this.txobj.outs.push(txout);
|
txobj.outs.push(txout);
|
||||||
this.remainderSat = remainderSat;
|
this.remainderSat = remainderSat;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
TransactionBuilder.prototype._setFeeAndRemainder = function() {
|
TransactionBuilder.prototype._setFeeAndRemainder = function(txobj) {
|
||||||
|
|
||||||
//starting size estimation
|
//starting size estimation
|
||||||
var size = 500, maxSizeK, remainderIndex = this.txobj.outs.length;
|
var size = 500, maxSizeK, remainderIndex = txobj.outs.length;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// based on https://en.bitcoin.it/wiki/Transaction_fees
|
// based on https://en.bitcoin.it/wiki/Transaction_fees
|
||||||
maxSizeK = parseInt(size / 1000) + 1;
|
maxSizeK = parseInt(size / 1000) + 1;
|
||||||
|
@ -355,12 +349,12 @@ TransactionBuilder.prototype._setFeeAndRemainder = function() {
|
||||||
var neededAmountSat = this.valueOutSat.add(feeSat);
|
var neededAmountSat = this.valueOutSat.add(feeSat);
|
||||||
|
|
||||||
this._selectUnspent(neededAmountSat)
|
this._selectUnspent(neededAmountSat)
|
||||||
._setInputs()
|
._setInputs(txobj)
|
||||||
._setFee(feeSat)
|
._setFee(feeSat)
|
||||||
._setRemainder(remainderIndex);
|
._setRemainder(txobj, remainderIndex);
|
||||||
|
|
||||||
|
|
||||||
size = new Transaction(this.txobj).getSize();
|
size = new Transaction(txobj).getSize();
|
||||||
} while (size > (maxSizeK + 1) * 1000);
|
} while (size > (maxSizeK + 1) * 1000);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
@ -368,9 +362,13 @@ TransactionBuilder.prototype._setFeeAndRemainder = function() {
|
||||||
TransactionBuilder.prototype.setOutputs = function(outs) {
|
TransactionBuilder.prototype.setOutputs = function(outs) {
|
||||||
var valueOutSat = bignum(0);
|
var valueOutSat = bignum(0);
|
||||||
|
|
||||||
this.txobj.outs = [];
|
var txobj = {};
|
||||||
var l =outs.length;
|
txobj.version = 1;
|
||||||
|
txobj.lock_time = this.lockTime || 0;
|
||||||
|
txobj.ins = [];
|
||||||
|
txobj.outs = [];
|
||||||
|
|
||||||
|
var l =outs.length;
|
||||||
for (var i = 0; i < l; i++) {
|
for (var i = 0; i < l; i++) {
|
||||||
var amountSat = outs[i].amountSat || util.parseValue(outs[i].amount);
|
var amountSat = outs[i].amountSat || util.parseValue(outs[i].amount);
|
||||||
var value = util.bigIntToValue(amountSat);
|
var value = util.bigIntToValue(amountSat);
|
||||||
|
@ -379,7 +377,7 @@ TransactionBuilder.prototype.setOutputs = function(outs) {
|
||||||
v: value,
|
v: value,
|
||||||
s: script.getBuffer(),
|
s: script.getBuffer(),
|
||||||
};
|
};
|
||||||
this.txobj.outs.push(txout);
|
txobj.outs.push(txout);
|
||||||
|
|
||||||
var sat = outs[i].amountSat || util.parseValue(outs[i].amount);
|
var sat = outs[i].amountSat || util.parseValue(outs[i].amount);
|
||||||
valueOutSat = valueOutSat.add(sat);
|
valueOutSat = valueOutSat.add(sat);
|
||||||
|
@ -387,9 +385,9 @@ TransactionBuilder.prototype.setOutputs = function(outs) {
|
||||||
|
|
||||||
this.valueOutSat = valueOutSat;
|
this.valueOutSat = valueOutSat;
|
||||||
|
|
||||||
this._setFeeAndRemainder();
|
this._setFeeAndRemainder(txobj);
|
||||||
|
|
||||||
this.tx = new Transaction(this.txobj);
|
this.tx = new Transaction(txobj);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -506,12 +504,12 @@ TransactionBuilder.prototype._signPubKeyHash = function(walletKeyMap, input, txS
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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, scriptSig.chunks[i]);
|
||||||
// }
|
}
|
||||||
// };
|
};
|
||||||
|
|
||||||
TransactionBuilder.prototype._initMultiSig = function(scriptSig, nreq) {
|
TransactionBuilder.prototype._initMultiSig = function(scriptSig, nreq) {
|
||||||
var wasUpdated = false;
|
var wasUpdated = false;
|
||||||
|
@ -526,7 +524,8 @@ TransactionBuilder.prototype._initMultiSig = function(scriptSig, nreq) {
|
||||||
|
|
||||||
|
|
||||||
TransactionBuilder.prototype._isSignedWithKey = function(wk, scriptSig, txSigHash, nreq) {
|
TransactionBuilder.prototype._isSignedWithKey = function(wk, scriptSig, txSigHash, nreq) {
|
||||||
var ret=0;
|
var ret=false;
|
||||||
|
// _dumpChunks(scriptSig);
|
||||||
for(var i=1; i<=nreq; i++) {
|
for(var i=1; i<=nreq; i++) {
|
||||||
var chunk = scriptSig.chunks[i];
|
var chunk = scriptSig.chunks[i];
|
||||||
if (chunk ===0 || chunk.length === 0) continue;
|
if (chunk ===0 || chunk.length === 0) continue;
|
||||||
|
@ -567,6 +566,7 @@ TransactionBuilder.prototype._updateMultiSig = function(wk, scriptSig, txSigHash
|
||||||
wasUpdated=true;
|
wasUpdated=true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// _dumpChunks(scriptSig); // TODO
|
||||||
return wasUpdated ? scriptSig : null;
|
return wasUpdated ? scriptSig : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -683,8 +683,6 @@ TransactionBuilder.prototype.sign = function(keys) {
|
||||||
l = ins.length,
|
l = ins.length,
|
||||||
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];
|
||||||
|
|
||||||
|
@ -717,5 +715,223 @@ TransactionBuilder.prototype.build = function() {
|
||||||
return this.tx;
|
return this.tx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TransactionBuilder.prototype.toObj = function() {
|
||||||
|
var data = {
|
||||||
|
valueInSat : this.valueInSat.toString(),
|
||||||
|
valueOutSat : this.valueOutSat.toString(),
|
||||||
|
feeSat : this.feeSat.toString(),
|
||||||
|
remainderSat : this.remainderSat.toString(),
|
||||||
|
|
||||||
|
hashToScriptMap : this.hashToScriptMap,
|
||||||
|
selectedUtxos : this.selectedUtxos,
|
||||||
|
|
||||||
|
inputsSigned : this.inputsSigned,
|
||||||
|
signaturesAdded : this.signaturesAdded,
|
||||||
|
|
||||||
|
//opts :
|
||||||
|
signhash : this.signhash,
|
||||||
|
spendUnconfirmed : this.spendUnconfirmed,
|
||||||
|
};
|
||||||
|
if (this.tx) {
|
||||||
|
data.tx =this.tx.serialize().toString('hex');
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TransactionBuilder.fromObj = function(data) {
|
||||||
|
var b = new TransactionBuilder();
|
||||||
|
b.valueInSat = data.valueInSat.toString();
|
||||||
|
b.valueOutSat = data.valueOutSat.toString();
|
||||||
|
b.feeSat = data.feeSat.toString();
|
||||||
|
b.remainderSat = data.remainderSat.toString();
|
||||||
|
|
||||||
|
b.hashToScriptMap = data.hashToScriptMap;
|
||||||
|
b.selectedUtxos = data.selectedUtxos;
|
||||||
|
|
||||||
|
b.inputsSigned = data.inputsSigned;
|
||||||
|
b.signaturesAdded = data.signaturesAdded;
|
||||||
|
|
||||||
|
b.signhash = data.signhash;
|
||||||
|
b.spendUnconfirmed = data.spendUnconfirmed;
|
||||||
|
|
||||||
|
b._setInputMap();
|
||||||
|
|
||||||
|
if (data.tx) {
|
||||||
|
// Tx may have signatures, that are not on txobj
|
||||||
|
var t = new Transaction();
|
||||||
|
t.parse(new Buffer(data.tx,'hex'));
|
||||||
|
b.tx = t;
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TransactionBuilder.prototype._checkMergeability = function(b) {
|
||||||
|
var self=this;
|
||||||
|
|
||||||
|
// Builder should have the same params
|
||||||
|
['valueInSat', 'valueOutSat', 'feeSat', 'remainderSat', 'signhash', 'spendUnconfirmed']
|
||||||
|
.forEach(function (k) {
|
||||||
|
|
||||||
|
if (self[k].toString() !== b[k].toString()) {
|
||||||
|
throw new Error('mismatch at TransactionBuilder match: '
|
||||||
|
+ k + ': ' + self[k] + ' vs. ' + b[k]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (self.hashToScriptMap) {
|
||||||
|
var err = 0;
|
||||||
|
if(! b.hashToScriptMap) err=1;
|
||||||
|
Object.keys(self.hashToScriptMap).forEach(function(k) {
|
||||||
|
if (!b.hashToScriptMap[k]) err=1;
|
||||||
|
if (self.hashToScriptMap[k] !== b.hashToScriptMap[k]) err=1;
|
||||||
|
});
|
||||||
|
if (err)
|
||||||
|
throw new Error('mismatch at TransactionBuilder hashToScriptMap');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var err = 0, i=0;;
|
||||||
|
self.selectedUtxos.forEach(function(u) {
|
||||||
|
if (!err) {
|
||||||
|
var v=b.selectedUtxos[i++];
|
||||||
|
if (!v) err=1;
|
||||||
|
// confirmations could differ
|
||||||
|
['address', 'hash', 'scriptPubKey', 'vout', 'amount'].forEach(function(k) {
|
||||||
|
if (u[k] !== v[k])
|
||||||
|
err=k;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (err)
|
||||||
|
throw new Error('mismatch at TransactionBuilder selectedUtxos #' + i-1+ ' Key:' + err);
|
||||||
|
|
||||||
|
|
||||||
|
err = 0; i=0;;
|
||||||
|
self.inputMap.forEach(function(u) {
|
||||||
|
if (!err) {
|
||||||
|
var v=b.inputMap[i++];
|
||||||
|
if (!v) err=1;
|
||||||
|
// confirmations could differ
|
||||||
|
['address', 'scriptType', 'scriptPubKey', 'i'].forEach(function(k) {
|
||||||
|
if (u[k].toString() !== v[k].toString())
|
||||||
|
err=k;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (err)
|
||||||
|
throw new Error('mismatch at TransactionBuilder inputMap #' + i-1 + ' Key:' + err);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// this assumes that the same signature can not be v0 / v1 (which shouldnt be!)
|
||||||
|
TransactionBuilder.prototype._mergeInputSig = function(s0buf, s1buf, ignoreConflictingSignatures) {
|
||||||
|
if (buffertools.compare(s0buf,s1buf) === 0) {
|
||||||
|
//console.log('BUFFERS .s MATCH'); //TODO
|
||||||
|
return s0buf;
|
||||||
|
}
|
||||||
|
// Is multisig?
|
||||||
|
var s0 = new Script(s0buf);
|
||||||
|
var s1 = new Script(s1buf);
|
||||||
|
var l0 = s0.chunks.length;
|
||||||
|
var l1 = s1.chunks.length;
|
||||||
|
var s0map = {};
|
||||||
|
|
||||||
|
if (l0 && l1 && l0 !== l1)
|
||||||
|
throw new Error('TX sig types mismatch in merge');
|
||||||
|
|
||||||
|
if (!l0 && !l1) return s0buf;
|
||||||
|
if ( l0 && !l1) return s0buf;
|
||||||
|
if (!l0 && l1) return s1buf;
|
||||||
|
|
||||||
|
// Look for differences.
|
||||||
|
for (var i=0; i<l0; i++) {
|
||||||
|
if (!this._chunkIsEmpty(s0.chunks[i]))
|
||||||
|
s0map[s0.chunks[i]] = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
var diff = [];
|
||||||
|
for (var i=0; i<l1; i++) {
|
||||||
|
if ( !this._chunkIsEmpty(s1.chunks[i]) && !s0map[s1.chunks[i]]) {
|
||||||
|
diff.push(s1.chunks[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!diff) {
|
||||||
|
console.log('[TransactionBuilder.js.857: NO DIFF FOUND, just ORDER DIFF]'); //TODO
|
||||||
|
return s0.getBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
var emptySlots = [];
|
||||||
|
for (var i=1; i<l0; i++) {
|
||||||
|
if (this._chunkIsEmpty(s0.chunks[i])) {
|
||||||
|
emptySlots.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
var v0 = this.tx;
|
||||||
|
var v1 = tx;
|
||||||
|
|
||||||
|
var l = v0.ins.length;
|
||||||
|
if (l !== v1.ins.length)
|
||||||
|
throw new Error('TX in length mismatch in merge');
|
||||||
|
|
||||||
|
this.inputsSigned =0;
|
||||||
|
for(var i=0; i<l; i++) {
|
||||||
|
var i0 = v0.ins[i];
|
||||||
|
var i1 = v1.ins[i];
|
||||||
|
|
||||||
|
if (i0.q !== i1.q)
|
||||||
|
throw new Error('TX sequence ins mismatch in merge. Input:',i);
|
||||||
|
|
||||||
|
if (buffertools.compare(i0.o,i1.o) !== 0)
|
||||||
|
throw new Error('TX .o in mismatch in merge. Input:',i);
|
||||||
|
|
||||||
|
i0.s=this._mergeInputSig(i0.s,i1.s, ignoreConflictingSignatures);
|
||||||
|
|
||||||
|
if (v0.isInputComplete(i)) this.inputsSigned++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TransactionBuilder.prototype.merge = function(b, ignoreConflictingSignatures) {
|
||||||
|
this._checkMergeability(b);
|
||||||
|
|
||||||
|
|
||||||
|
// Does this tX have any signature already?
|
||||||
|
if (this.tx || b.tx) {
|
||||||
|
if (this.tx.getNormalizedHash().toString('hex')
|
||||||
|
!== b.tx.getNormalizedHash().toString('hex'))
|
||||||
|
throw new Error('mismatch at TransactionBuilder NTXID');
|
||||||
|
|
||||||
|
this._mergeTx(b.tx, ignoreConflictingSignatures);
|
||||||
|
// TODO UPDATE: signaturesAdded, inputsSigned
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = require('soop')(TransactionBuilder);
|
module.exports = require('soop')(TransactionBuilder);
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
"url": "https://github.com/bitpay/bitcore.git"
|
"url": "https://github.com/bitpay/bitcore.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"install": "node-gyp rebuild",
|
||||||
"test": "mocha test -R spec",
|
"test": "mocha test -R spec",
|
||||||
"coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --reporter spec test",
|
"coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --reporter spec test",
|
||||||
"postinstall": "node browser/build.js -a"
|
"postinstall": "node browser/build.js -a"
|
||||||
|
|
|
@ -6,15 +6,10 @@ var bitcore = bitcore || require('../bitcore');
|
||||||
|
|
||||||
var should = chai.should();
|
var should = chai.should();
|
||||||
|
|
||||||
var Transaction = bitcore.Transaction;
|
|
||||||
var TransactionBuilder = bitcore.TransactionBuilder;
|
var TransactionBuilder = bitcore.TransactionBuilder;
|
||||||
var In;
|
|
||||||
var Out;
|
|
||||||
var Script = bitcore.Script;
|
|
||||||
var WalletKey = bitcore.WalletKey;
|
var WalletKey = bitcore.WalletKey;
|
||||||
var util = bitcore.util;
|
var util = bitcore.util;
|
||||||
var networks = bitcore.networks;
|
var networks = bitcore.networks;
|
||||||
var buffertools = require('buffertools');
|
|
||||||
var testdata = testdata || require('./testdata');
|
var testdata = testdata || require('./testdata');
|
||||||
|
|
||||||
describe('TransactionBuilder', function() {
|
describe('TransactionBuilder', function() {
|
||||||
|
@ -31,9 +26,9 @@ describe('TransactionBuilder', function() {
|
||||||
it('should be able to create instance with params', function() {
|
it('should be able to create instance with params', function() {
|
||||||
var t = new TransactionBuilder({spendUnconfirmed: true, lockTime: 10});
|
var t = new TransactionBuilder({spendUnconfirmed: true, lockTime: 10});
|
||||||
should.exist(t);
|
should.exist(t);
|
||||||
should.exist(t.txobj.version);
|
should.exist(t.lockTime);
|
||||||
t.spendUnconfirmed.should.equal(true);
|
t.spendUnconfirmed.should.equal(true);
|
||||||
t.txobj.lock_time.should.equal(10);
|
t.lockTime.should.equal(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,22 +78,24 @@ describe('TransactionBuilder', function() {
|
||||||
|
|
||||||
|
|
||||||
it('#_setInputs sets inputs', function() {
|
it('#_setInputs sets inputs', function() {
|
||||||
|
var txobj={};
|
||||||
var b = getBuilder()
|
var b = getBuilder()
|
||||||
.setUnspent(testdata.dataUnspent)
|
.setUnspent(testdata.dataUnspent)
|
||||||
._selectUnspent(0.1 * util.COIN)
|
._selectUnspent(0.1 * util.COIN)
|
||||||
._setInputs();
|
._setInputs(txobj);
|
||||||
|
|
||||||
should.exist(b.txobj.ins[0].s);
|
should.exist(txobj.ins[0].s);
|
||||||
should.exist(b.txobj.ins[0].q);
|
should.exist(txobj.ins[0].q);
|
||||||
should.exist(b.txobj.ins[0].o);
|
should.exist(txobj.ins[0].o);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#_setInputMap set inputMap', function() {
|
it('#_setInputMap set inputMap', function() {
|
||||||
|
var txobj={};
|
||||||
var b = getBuilder()
|
var b = getBuilder()
|
||||||
.setUnspent(testdata.dataUnspent)
|
.setUnspent(testdata.dataUnspent)
|
||||||
._selectUnspent(0.1 * util.COIN)
|
._selectUnspent(0.1 * util.COIN)
|
||||||
._setInputs()
|
._setInputs(txobj)
|
||||||
._setInputMap();
|
._setInputMap(txobj);
|
||||||
|
|
||||||
should.exist(b.inputMap);
|
should.exist(b.inputMap);
|
||||||
b.inputMap.length.should.equal(2);
|
b.inputMap.length.should.equal(2);
|
||||||
|
@ -644,7 +641,7 @@ describe('TransactionBuilder', function() {
|
||||||
b.signaturesAdded.should.equal(3);
|
b.signaturesAdded.should.equal(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should sign in steps a p2sh/p2pubkeyhash tx', function() {
|
it('should sign a p2sh/p2pubkeyhash tx', function() {
|
||||||
var priv = 'cMpKwGr5oxEacN95WFKNEq6tTcvi11regFwS3muHvGYVxMPJX8JA';
|
var priv = 'cMpKwGr5oxEacN95WFKNEq6tTcvi11regFwS3muHvGYVxMPJX8JA';
|
||||||
var network = 'testnet';
|
var network = 'testnet';
|
||||||
var opts = {
|
var opts = {
|
||||||
|
@ -682,10 +679,181 @@ describe('TransactionBuilder', function() {
|
||||||
b.setHashToScriptMap(hashMap);
|
b.setHashToScriptMap(hashMap);
|
||||||
b.sign([priv]);
|
b.sign([priv]);
|
||||||
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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('#toObj #fromObj roundtrip', function() {
|
||||||
|
var b = getBuilder2();
|
||||||
|
|
||||||
|
b.isFullySigned().should.equal(false);
|
||||||
|
b.getSelectedUnspent().length.should.equal(2);
|
||||||
|
|
||||||
|
var data =b.toObj();
|
||||||
|
|
||||||
|
var b2 = TransactionBuilder.fromObj(data);
|
||||||
|
var tx = b2.build();
|
||||||
|
should.exist(tx);
|
||||||
|
tx.version.should.equal(1);
|
||||||
|
tx.ins.length.should.equal(2);
|
||||||
|
tx.outs.length.should.equal(2);
|
||||||
|
|
||||||
|
util.valueToBigInt(tx.outs[0].v).cmp(8000000).should.equal(0);
|
||||||
|
|
||||||
|
// remainder is 0.0299 here because unspent select utxos in order
|
||||||
|
util.valueToBigInt(tx.outs[1].v).cmp(2990000).should.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#toObj #fromObj roundtrip, step signatures p2sh/p2pubkeyhash', 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.signaturesAdded.should.equal(0);
|
||||||
|
|
||||||
|
var b2 = TransactionBuilder.fromObj(b.toObj());
|
||||||
|
|
||||||
|
b2.sign(k1);
|
||||||
|
|
||||||
|
b2.isFullySigned().should.equal(false);
|
||||||
|
b2.signaturesAdded.should.equal(1);
|
||||||
|
|
||||||
|
var tx = b2.build();
|
||||||
|
tx.ins.length.should.equal(1);
|
||||||
|
tx.outs.length.should.equal(2);
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
b2.signaturesAdded.should.equal(1);
|
||||||
|
|
||||||
|
// Sign with the same
|
||||||
|
var b3 = TransactionBuilder.fromObj(b2.toObj());
|
||||||
|
|
||||||
|
b3.sign(k1);
|
||||||
|
b3.isFullySigned().should.equal(false);
|
||||||
|
b3.signaturesAdded.should.equal(1);
|
||||||
|
|
||||||
|
// Sign with k5
|
||||||
|
var b4 = TransactionBuilder.fromObj(b3.toObj());
|
||||||
|
b4.sign(k5);
|
||||||
|
b4.isFullySigned().should.equal(false);
|
||||||
|
b4.signaturesAdded.should.equal(2);
|
||||||
|
|
||||||
|
var b5 = TransactionBuilder.fromObj(b4.toObj());
|
||||||
|
// Sign k2
|
||||||
|
b5.sign(k2);
|
||||||
|
b5.isFullySigned().should.equal(true);
|
||||||
|
var tx2 = b5.build();
|
||||||
|
tx2.isComplete().should.equal(true);
|
||||||
|
b5.signaturesAdded.should.equal(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#merge self', function() {
|
||||||
|
var b = getBuilder3([{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 16
|
||||||
|
}])
|
||||||
|
.sign(testdata.dataUnspentSign.keyStrings);
|
||||||
|
b.merge(b);
|
||||||
|
|
||||||
|
b.isFullySigned().should.equal(true);
|
||||||
|
var tx = b.build();
|
||||||
|
tx.isComplete().should.equal(true);
|
||||||
|
tx.ins.length.should.equal(3);
|
||||||
|
tx.outs.length.should.equal(2);
|
||||||
|
});
|
||||||
|
it('#merge simple', function() {
|
||||||
|
var b = getBuilder3([{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 16
|
||||||
|
}])
|
||||||
|
.sign(testdata.dataUnspentSign.keyStrings);
|
||||||
|
|
||||||
|
// merge simple
|
||||||
|
var b2 = getBuilder3([{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 16
|
||||||
|
}]);
|
||||||
|
b2.isFullySigned().should.equal(false);
|
||||||
|
b2.merge(b);
|
||||||
|
|
||||||
|
b2.isFullySigned().should.equal(true);
|
||||||
|
var tx = b.build();
|
||||||
|
tx.isComplete().should.equal(true);
|
||||||
|
tx.ins.length.should.equal(3);
|
||||||
|
tx.outs.length.should.equal(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#merge checks', function() {
|
||||||
|
var b = getBuilder3([{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 16
|
||||||
|
}]);
|
||||||
|
// bad amount
|
||||||
|
var b2 = getBuilder3([{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 15
|
||||||
|
}]);
|
||||||
|
(function() {b2.merge(b);}).should.throw();
|
||||||
|
// bad out
|
||||||
|
b2 = getBuilder3([{
|
||||||
|
address: 'muHct3YZ9Nd5Pq7uLYYhXRAxeW4EnpcaLz',
|
||||||
|
amount: 16
|
||||||
|
}]);
|
||||||
|
(function() {b2.merge(b);}).should.throw();
|
||||||
|
|
||||||
|
// same signature
|
||||||
|
// -> this fails: no way to check signatures, since PRIV Keys are not stored
|
||||||
|
b = getBuilder3([{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 16
|
||||||
|
}])
|
||||||
|
.sign(testdata.dataUnspentSign.keyStrings);
|
||||||
|
// merge simple
|
||||||
|
b2 = getBuilder3([{
|
||||||
|
address: 'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE',
|
||||||
|
amount: 16
|
||||||
|
}])
|
||||||
|
.sign(testdata.dataUnspentSign.keyStrings);
|
||||||
|
(function() {b2.merge(b);}).should.throw();
|
||||||
|
b2.merge(b, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#merge p2sh/steps', function() {
|
||||||
|
var b = getP2shBuilder(1);
|
||||||
|
var k1 = testdata.dataUnspentSign.keyStringsP2sh.slice(0,1);
|
||||||
|
var k2 = testdata.dataUnspentSign.keyStringsP2sh.slice(1,2);
|
||||||
|
var k3 = testdata.dataUnspentSign.keyStringsP2sh.slice(2,3);
|
||||||
|
b.isFullySigned().should.equal(false);
|
||||||
|
b.signaturesAdded.should.equal(0);
|
||||||
|
b.sign(k1);
|
||||||
|
b.signaturesAdded.should.equal(1);
|
||||||
|
b.isFullySigned().should.equal(false);
|
||||||
|
var tx = b.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
|
||||||
|
var b2 = getP2shBuilder(1);
|
||||||
|
b2.sign(k2);
|
||||||
|
b2.signaturesAdded.should.equal(1);
|
||||||
|
b2.merge(b);
|
||||||
|
b2.signaturesAdded.should.equal(2);
|
||||||
|
tx = b2.build();
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
|
||||||
|
var b3 = getP2shBuilder(1);
|
||||||
|
b3.sign(k3);
|
||||||
|
b3.signaturesAdded.should.equal(1);
|
||||||
|
b3.merge(b2);
|
||||||
|
b3.signaturesAdded.should.equal(3);
|
||||||
|
tx = b3.build();
|
||||||
|
tx.isComplete().should.equal(true);
|
||||||
|
|
||||||
|
b2.merge(b3);
|
||||||
|
b2.signaturesAdded.should.equal(3);
|
||||||
|
tx = b2.build();
|
||||||
|
tx.isComplete().should.equal(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue