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
},
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: {

View File

@ -1,80 +1,51 @@
/*
var tx = (new TransactionBuilder(opts))
.setUnspent(utxos)
.setOutputs(outs)
.sign(keys)
.build();
var builder = (new TransactionBuilder(opts))
.setUnspent(spent)
.setOutputs(outs);
// Uncomplete tx (no signed or partially signed)
var tx = builder.build();
..later..
builder.sign(keys);
while ( builder.isFullySigned() ) {
... get new keys ...
builder.sign(keys);
}
var tx = builder.build();
broadcast(tx.serialize());
To get selected unspent outputs:
var selectedUnspent = builder.getSelectedUnspent();
@unspent
* unspent outputs array (UTXO), using the following format:
* [{
* 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.
*
* @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
*
*
*/
// Creates a bitcore Transaction object
//
//
// Synopsis
// --------
// ```
// var tx = (new TransactionBuilder(opts))
// .setUnspent(utxos)
// .setOutputs(outs)
// .sign(keys)
// .build();
//
//
// var builder = (new TransactionBuilder(opts))
// .setUnspent(spent)
// .setOutputs(outs);
//
// // Uncomplete tx (no signed or partially signed)
// var tx = builder.build();
//
// ..later..
//
// builder.sign(keys);
// while ( builder.isFullySigned() ) {
//
// ... get new keys ...
//
// builder.sign(keys);
// }
//
// var tx = builder.build();
// broadcast(tx.serialize());
//
// //Searialize it and pass it around...
// var string = JSON.serialize(builder.toObj());
// // then...
// var builder = TransactionBuilder.fromObj(JSON.parse(str);
// builder.sign(keys);
// // Also
// var builder2 = TransactionBuilder.fromObj(JSON.parse(str2);
// builder2.merge(builder); // Will merge signatures for p2sh mulsig txs.
//
//
// ```
//
//
//
'use strict';
@ -94,6 +65,35 @@ var log = imports.log || require('../util/log');
var Transaction = imports.Transaction || require('./Transaction');
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) {
opts = opts || {};
this.lockTime = opts.lockTime || 0;
@ -179,8 +179,27 @@ TransactionBuilder.infoForP2sh = function(opts, networkName) {
};
};
TransactionBuilder.prototype.setUnspent = function(utxos) {
this.utxos = utxos;
// setUnspent
// ----------
// 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;
};
@ -209,6 +228,12 @@ TransactionBuilder.prototype._setInputMap = function() {
return this;
};
// getSelectedUnspent
// ------------------
//
// Returns the selected unspent outputs, to be used in the transaction.
TransactionBuilder.prototype.getSelectedUnspent = function() {
return this.selectedUtxos;
};
@ -312,12 +337,12 @@ TransactionBuilder.prototype._setRemainder = function(txobj, remainderIndex) {
typeof this.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 l =txobj.outs.length;
this.remainderSat = bignum(0);
//remove old remainder?
/*remove old remainder? */
if (l > remainderIndex) {
txobj.outs.pop();
}
@ -339,10 +364,10 @@ TransactionBuilder.prototype._setRemainder = function(txobj, remainderIndex) {
TransactionBuilder.prototype._setFeeAndRemainder = function(txobj) {
//starting size estimation
/* starting size estimation */
var size = 500, maxSizeK, remainderIndex = txobj.outs.length;
do {
// based on https://en.bitcoin.it/wiki/Transaction_fees
/* based on https://en.bitcoin.it/wiki/Transaction_fees */
maxSizeK = parseInt(size / 1000) + 1;
var feeSat = this.givenFeeSat ?
@ -361,6 +386,21 @@ TransactionBuilder.prototype._setFeeAndRemainder = function(txobj) {
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) {
var valueOutSat = bignum(0);
@ -394,7 +434,7 @@ TransactionBuilder.prototype.setOutputs = function(outs) {
};
TransactionBuilder._mapKeys = function(keys) {
//prepare keys
/* prepare keys */
var walletKeyMap = {};
var l = keys.length;
var wk;
@ -505,15 +545,15 @@ TransactionBuilder.prototype._signPubKeyHash = function(walletKeyMap, input, txS
return {inputFullySigned: true, signaturesAdded: 1, 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, Buffer.isBuffer(scriptSig.chunks[i])
// ?scriptSig.chunks[i].toString('hex'):scriptSig.chunks[i] );
// }
// };
/* FOR TESTING
var _dumpChunks = function (scriptSig, label) {
console.log('## DUMP: ' + label + ' ##');
for(var i=0; i<scriptSig.chunks.length; i++) {
console.log('\tCHUNK ', i, Buffer.isBuffer(scriptSig.chunks[i])
?scriptSig.chunks[i].toString('hex'):scriptSig.chunks[i] );
}
};
*/
TransactionBuilder.prototype._chunkSignedWithKey = function(scriptSig, txSigHash, publicKey) {
var ret;
@ -599,7 +639,6 @@ TransactionBuilder.prototype._signMultiSig = function(walletKeyMap, input, txSig
var signaturesAdded = 0;
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]});
if (!wk) continue;
@ -609,8 +648,6 @@ TransactionBuilder.prototype._signMultiSig = function(walletKeyMap, input, txSig
signaturesAdded++;
}
}
if (scriptSig.countSignatures() === nreq) {
}
var ret = {
inputFullySigned: scriptSig.countSignatures() === nreq,
@ -621,7 +658,6 @@ TransactionBuilder.prototype._signMultiSig = function(walletKeyMap, input, txSig
};
var fnToSign = {};
TransactionBuilder.prototype._scriptIsAppended = function(script, scriptToAddBuf) {
var len = script.chunks.length;
@ -647,7 +683,7 @@ TransactionBuilder.prototype._addScript = function(scriptBuf, scriptToAddBuf) {
TransactionBuilder.prototype._getInputForP2sh = function(script, index) {
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;
switch(scriptType) {
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_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) {
this._checkTx();
var tx = this.tx,
@ -728,13 +778,24 @@ TransactionBuilder.prototype.sign = function(keys) {
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) {
this.hashToScriptMap= hashToScriptMap;
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() {
return this.inputsSigned === this.tx.ins.length;
};
@ -744,7 +805,13 @@ TransactionBuilder.prototype.build = function() {
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() {
var data = {
valueInSat : this.valueInSat.toString(),
@ -758,7 +825,6 @@ TransactionBuilder.prototype.toObj = function() {
inputsSigned : this.inputsSigned,
signaturesAdded : this.signaturesAdded,
//opts :
signhash : this.signhash,
spendUnconfirmed : this.spendUnconfirmed,
};
@ -768,6 +834,11 @@ TransactionBuilder.prototype.toObj = function() {
return data;
};
// fromObj
// -------
// Returns a TransactionBuilder instance given
// a plain Javascript object created previously
// with `.toObj`. See `.toObj`.
TransactionBuilder.fromObj = function(data) {
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) {
this._checkMergeability(b);