docco documentation

This commit is contained in:
Matias Alejo Garcia 2014-04-29 09:34:26 -03:00
parent 0c8a582522
commit ec2299362c
2 changed files with 177 additions and 101 deletions

View File

@ -17,7 +17,7 @@ module.exports = function(grunt) {
stderr: true stderr: true
}, },
command: grunt.option('target') === 'dev' ? command: grunt.option('target') === 'dev' ?
'node ./browser/build.js -a -d ' : 'node ./browser/build.js -a' 'node ./browser/build.js -a -d; docco lib/* ' : 'node ./browser/build.js -a'
} }
}, },
watch: { watch: {

View File

@ -1,80 +1,51 @@
/* // Creates a bitcore Transaction object
var tx = (new TransactionBuilder(opts)) //
.setUnspent(utxos) //
.setOutputs(outs) // Synopsis
.sign(keys) // --------
.build(); // ```
// var tx = (new TransactionBuilder(opts))
// .setUnspent(utxos)
var builder = (new TransactionBuilder(opts)) // .setOutputs(outs)
.setUnspent(spent) // .sign(keys)
.setOutputs(outs); // .build();
//
// Uncomplete tx (no signed or partially signed) //
var tx = builder.build(); // var builder = (new TransactionBuilder(opts))
// .setUnspent(spent)
..later.. // .setOutputs(outs);
//
builder.sign(keys); // // Uncomplete tx (no signed or partially signed)
while ( builder.isFullySigned() ) { // var tx = builder.build();
//
... get new keys ... // ..later..
//
builder.sign(keys); // builder.sign(keys);
} // while ( builder.isFullySigned() ) {
//
var tx = builder.build(); // ... get new keys ...
broadcast(tx.serialize()); //
// builder.sign(keys);
To get selected unspent outputs: // }
var selectedUnspent = builder.getSelectedUnspent(); //
// var tx = builder.build();
// broadcast(tx.serialize());
@unspent //
* unspent outputs array (UTXO), using the following format: // //Searialize it and pass it around...
* [{ // var string = JSON.serialize(builder.toObj());
* address: "mqSjTad2TKbPcKQ3Jq4kgCkKatyN44UMgZ", // // then...
* hash: "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1", // var builder = TransactionBuilder.fromObj(JSON.parse(str);
* scriptPubKey: "76a9146ce4e1163eb18939b1440c42844d5f0261c0338288ac", // builder.sign(keys);
* vout: 1, // // Also
* amount: 0.01, // var builder2 = TransactionBuilder.fromObj(JSON.parse(str2);
* confirmations: 3 // builder2.merge(builder); // Will merge signatures for p2sh mulsig txs.
* }, ... //
* ] //
* This is compatible con insight's utxo API. // ```
* That amount is in BTCs (as returned in insight and bitcoind). //
* amountSat (instead of amount) can be given to provide amount in satochis. //
* //
* @outs
* an array of [{
* address: xx,
* amount:0.001
* },...]
*
* @keys
* an array of strings representing private keys to sign the
* transaction in WIF private key format OR WalletKey objects
*
* @opts
* {
* remainderOut: null,
* fee: 0.001,
* lockTime: null,
* spendUnconfirmed: false,
* signhash: SIGHASH_ALL
* }
* Amounts are in BTC. instead of fee and amount; feeSat and amountSat can be given,
* repectively, to provide amounts in satoshis.
*
* If no remainderOut is given, and there are remainder coins, the
* first IN out will be used to return the coins. remainderOut has the form:
* remainderOut = { address: 1xxxxx}
* or
* remainderOut = { pubkeys: ['hex1','hex2',...} for multisig
*
*
*/
'use strict'; 'use strict';
@ -94,6 +65,35 @@ var log = imports.log || require('../util/log');
var Transaction = imports.Transaction || require('./Transaction'); 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);
// Methods
// -------
//
// TransactionBuilder
// ------------------
// Creates a TransactionBuilder instance
// `opts`
// ```
// {
// remainderOut: null,
// fee: 0.001,
// lockTime: null,
// spendUnconfirmed: false,
// signhash: SIGHASH_ALL
// }
// ```
// Amounts are in BTC. instead of fee and amount; feeSat and amountSat can be given,
// repectively, to provide amounts in satoshis.
//
// If no remainderOut is given, and there are remainder coins, the
// first IN out will be used to return the coins. remainderOut has the form:
// ```
// remainderOut = { address: 1xxxxx}
// ```
// or
// ```
// remainderOut = { pubkeys: ['hex1','hex2',...} for multisig
// ```
function TransactionBuilder(opts) { function TransactionBuilder(opts) {
opts = opts || {}; opts = opts || {};
this.lockTime = opts.lockTime || 0; this.lockTime = opts.lockTime || 0;
@ -179,8 +179,27 @@ TransactionBuilder.infoForP2sh = function(opts, networkName) {
}; };
}; };
TransactionBuilder.prototype.setUnspent = function(utxos) { // setUnspent
this.utxos = utxos; // ----------
// Sets the `unspent` available for the transaction. Some (or all)
// of them to fullfil the transaction's outputs and fee.
// The expected format is:
// ```
// [{
// address: "mqSjTad2TKbPcKQ3Jq4kgCkKatyN44UMgZ",
// hash: "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
// scriptPubKey: "76a9146ce4e1163eb18939b1440c42844d5f0261c0338288ac",
// vout: 1,
// amount: 0.01,
// confirmations: 3
// }, ...
// ]
// ```
// This is compatible con insight's utxo API.
// That amount is in BTCs (as returned in insight and bitcoind).
// amountSat (instead of amount) can be given to provide amount in satochis.
TransactionBuilder.prototype.setUnspent = function(unspent) {
this.utxos = unspent;
return this; return this;
}; };
@ -209,6 +228,12 @@ TransactionBuilder.prototype._setInputMap = function() {
return this; return this;
}; };
// getSelectedUnspent
// ------------------
//
// Returns the selected unspent outputs, to be used in the transaction.
TransactionBuilder.prototype.getSelectedUnspent = function() { TransactionBuilder.prototype.getSelectedUnspent = function() {
return this.selectedUtxos; return this.selectedUtxos;
}; };
@ -312,12 +337,12 @@ TransactionBuilder.prototype._setRemainder = function(txobj, remainderIndex) {
typeof this.valueOutSat === 'undefined') typeof this.valueOutSat === 'undefined')
throw new Error('valueInSat / valueOutSat undefined'); throw new Error('valueInSat / valueOutSat undefined');
// 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 =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) {
txobj.outs.pop(); txobj.outs.pop();
} }
@ -339,10 +364,10 @@ TransactionBuilder.prototype._setRemainder = function(txobj, remainderIndex) {
TransactionBuilder.prototype._setFeeAndRemainder = function(txobj) { TransactionBuilder.prototype._setFeeAndRemainder = function(txobj) {
//starting size estimation /* starting size estimation */
var size = 500, maxSizeK, remainderIndex = 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;
var feeSat = this.givenFeeSat ? var feeSat = this.givenFeeSat ?
@ -361,6 +386,21 @@ TransactionBuilder.prototype._setFeeAndRemainder = function(txobj) {
return this; return this;
}; };
// setOutputs
// ----------
// Sets the outputs for the transaction. Format is:
// ```
// an array of [{
// address: xx,
// amount:0.001
// },...]
// ```
//
// Note that only some of this outputs will be selected
// to create the transaction. The selected ones can be checked
// after calling `setOutputs`, with `.getSelectedUnspent`
//
TransactionBuilder.prototype.setOutputs = function(outs) { TransactionBuilder.prototype.setOutputs = function(outs) {
var valueOutSat = bignum(0); var valueOutSat = bignum(0);
@ -394,7 +434,7 @@ TransactionBuilder.prototype.setOutputs = function(outs) {
}; };
TransactionBuilder._mapKeys = function(keys) { TransactionBuilder._mapKeys = function(keys) {
//prepare keys /* prepare keys */
var walletKeyMap = {}; var walletKeyMap = {};
var l = keys.length; var l = keys.length;
var wk; var wk;
@ -505,15 +545,15 @@ TransactionBuilder.prototype._signPubKeyHash = function(walletKeyMap, input, txS
return {inputFullySigned: true, signaturesAdded: 1, script: scriptSig.getBuffer()}; return {inputFullySigned: true, signaturesAdded: 1, script: scriptSig.getBuffer()};
}; };
// 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, Buffer.isBuffer(scriptSig.chunks[i]) console.log('\tCHUNK ', i, Buffer.isBuffer(scriptSig.chunks[i])
// ?scriptSig.chunks[i].toString('hex'):scriptSig.chunks[i] ); ?scriptSig.chunks[i].toString('hex'):scriptSig.chunks[i] );
// } }
// }; };
*/
TransactionBuilder.prototype._chunkSignedWithKey = function(scriptSig, txSigHash, publicKey) { TransactionBuilder.prototype._chunkSignedWithKey = function(scriptSig, txSigHash, publicKey) {
var ret; var ret;
@ -599,7 +639,6 @@ TransactionBuilder.prototype._signMultiSig = function(walletKeyMap, input, txSig
var signaturesAdded = 0; var signaturesAdded = 0;
for(var j=0; j<l && scriptSig.countSignatures() < nreq ; j++) { for(var j=0; j<l && scriptSig.countSignatures() < nreq ; j++) {
//console.log('[TransactionBuilder.js] pubkey [j]',j, pubkeys[j].toString('hex')); //TODO
var wk = this._findWalletKey(walletKeyMap, {pubKeyBuf: pubkeys[j]}); var wk = this._findWalletKey(walletKeyMap, {pubKeyBuf: pubkeys[j]});
if (!wk) continue; if (!wk) continue;
@ -609,8 +648,6 @@ TransactionBuilder.prototype._signMultiSig = function(walletKeyMap, input, txSig
signaturesAdded++; signaturesAdded++;
} }
} }
if (scriptSig.countSignatures() === nreq) {
}
var ret = { var ret = {
inputFullySigned: scriptSig.countSignatures() === nreq, inputFullySigned: scriptSig.countSignatures() === nreq,
@ -621,7 +658,6 @@ TransactionBuilder.prototype._signMultiSig = function(walletKeyMap, input, txSig
}; };
var fnToSign = {}; var fnToSign = {};
TransactionBuilder.prototype._scriptIsAppended = function(script, scriptToAddBuf) { TransactionBuilder.prototype._scriptIsAppended = function(script, scriptToAddBuf) {
var len = script.chunks.length; var len = script.chunks.length;
@ -647,7 +683,7 @@ TransactionBuilder.prototype._addScript = function(scriptBuf, scriptToAddBuf) {
TransactionBuilder.prototype._getInputForP2sh = function(script, index) { TransactionBuilder.prototype._getInputForP2sh = function(script, index) {
var scriptType = script.classify(); var scriptType = script.classify();
// pubKeyHash is needed for TX_PUBKEYHASH and TX_PUBKEY to retrieve the keys. /* pubKeyHash is needed for TX_PUBKEYHASH and TX_PUBKEY to retrieve the keys. */
var pubKeyHash; var pubKeyHash;
switch(scriptType) { switch(scriptType) {
case Script.TX_PUBKEYHASH: case Script.TX_PUBKEYHASH:
@ -705,6 +741,20 @@ fnToSign[Script.TX_PUBKEY] = TransactionBuilder.prototype._signPubKey;
fnToSign[Script.TX_MULTISIG] = TransactionBuilder.prototype._signMultiSig; fnToSign[Script.TX_MULTISIG] = TransactionBuilder.prototype._signMultiSig;
fnToSign[Script.TX_SCRIPTHASH] = TransactionBuilder.prototype._signScriptHash; fnToSign[Script.TX_SCRIPTHASH] = TransactionBuilder.prototype._signScriptHash;
// sign
// ----
// Signs a transaction.
// `keys`: an array of strings representing private keys to sign the
// transaction in WIF private key format OR bitcore's `WalletKey` objects
//
// If multiple keys are given, each will be tested against the transaction's
// scriptPubKeys. Only the valid private keys will be used to sign.
// This method is fully compatible with *multisig* transactions.
//
// `.isFullySigned` can be queried to check is the transactions have all the needed
// signatures.
//
//
TransactionBuilder.prototype.sign = function(keys) { TransactionBuilder.prototype.sign = function(keys) {
this._checkTx(); this._checkTx();
var tx = this.tx, var tx = this.tx,
@ -728,13 +778,24 @@ TransactionBuilder.prototype.sign = function(keys) {
return this; return this;
}; };
// [ { address:scriptHex }] // setHashToScriptMap
// ------------------
// Needed for setup Address to Script maps
// for p2sh transactions. See `.infoForP2sh`
// for generate the input for this call.
//
TransactionBuilder.prototype.setHashToScriptMap = function(hashToScriptMap) { TransactionBuilder.prototype.setHashToScriptMap = function(hashToScriptMap) {
this.hashToScriptMap= hashToScriptMap; this.hashToScriptMap= hashToScriptMap;
return this; return this;
}; };
// isFullySigned
// -------------
// Checks if the transaction have all the necesary signatures.
// Also, `.signaturesAdded` and `.inputsSigned` can be queried
// for more information about the transaction signature status.
//
TransactionBuilder.prototype.isFullySigned = function() { TransactionBuilder.prototype.isFullySigned = function() {
return this.inputsSigned === this.tx.ins.length; return this.inputsSigned === this.tx.ins.length;
}; };
@ -744,7 +805,13 @@ TransactionBuilder.prototype.build = function() {
return this.tx; return this.tx;
}; };
// toObj
// -----
// Returns a plain Javascript object that contains
// the full status of the TransactionBuilder instance,
// suitable for serialization, storage and transmition.
// See `.fromObj`
//
TransactionBuilder.prototype.toObj = function() { TransactionBuilder.prototype.toObj = function() {
var data = { var data = {
valueInSat : this.valueInSat.toString(), valueInSat : this.valueInSat.toString(),
@ -758,7 +825,6 @@ TransactionBuilder.prototype.toObj = function() {
inputsSigned : this.inputsSigned, inputsSigned : this.inputsSigned,
signaturesAdded : this.signaturesAdded, signaturesAdded : this.signaturesAdded,
//opts :
signhash : this.signhash, signhash : this.signhash,
spendUnconfirmed : this.spendUnconfirmed, spendUnconfirmed : this.spendUnconfirmed,
}; };
@ -768,6 +834,11 @@ TransactionBuilder.prototype.toObj = function() {
return data; return data;
}; };
// fromObj
// -------
// Returns a TransactionBuilder instance given
// a plain Javascript object created previously
// with `.toObj`. See `.toObj`.
TransactionBuilder.fromObj = function(data) { TransactionBuilder.fromObj = function(data) {
var b = new TransactionBuilder(); var b = new TransactionBuilder();
@ -950,6 +1021,11 @@ TransactionBuilder.prototype._mergeTx = function(tx, ignoreConflictingSignatures
} }
}; };
// merge
// -----
// Merge to TransactionBuilder objects, merging inputs signatures.
// This function supports multisig p2sh inputs.
TransactionBuilder.prototype.merge = function(b) { TransactionBuilder.prototype.merge = function(b) {
this._checkMergeability(b); this._checkMergeability(b);