diff --git a/Address.js b/Address.js index 07333da..f8dcf8e 100644 --- a/Address.js +++ b/Address.js @@ -1,6 +1,7 @@ 'use strict'; var imports = require('soop').imports(); var parent = imports.parent || require('./util/VersionedData'); +var networks= imports.networks || require('./networks'); function Address() { Address.super(this, arguments); @@ -22,4 +23,19 @@ Address.prototype.isValid = function() { return answer; }; +Address.prototype.network = function() { + var version = this.version(); + + var livenet = networks.livenet; + var testnet = networks.testnet; + + var answer; + if (version === livenet.addressPubkey || version === livenet.addressScript) + answer = livenet; + else if (version === testnet.addressPubkey || version === testnet.addressScript) + answer = testnet; + + return answer; +}; + module.exports = require('soop')(Address); diff --git a/Transaction.js b/Transaction.js index 1da4964..5e04e13 100644 --- a/Transaction.js +++ b/Transaction.js @@ -11,8 +11,10 @@ var Parser = imports.Parser || require('./util/BinaryParser'); var Step = imports.Step || require('step'); var buffertools = imports.buffertools || require('buffertools'); var error = imports.error || require('./util/error'); +var networks = imports.networks || require('./networks'); var COINBASE_OP = Buffer.concat([util.NULL_HASH, new Buffer('FFFFFFFF', 'hex')]); +var DEFAULT_FEE = 0.0001; function TransactionIn(data) { if ("object" !== typeof data) { @@ -605,36 +607,7 @@ Transaction.prototype.fromObj = function fromObj(obj) { txobj.ins = []; txobj.outs = []; - obj.inputs.forEach(function(inputobj) { - var txin = new TransactionIn(); - txin.s = util.EMPTY_BUFFER; - txin.q = 0xffffffff; - - var hash = new Buffer(inputobj.txid, 'hex'); - hash = buffertools.reverse(hash); - var vout = parseInt(inputobj.vout); - var voutBuf = new Buffer(4); - voutBuf.writeUInt32LE(vout, 0); - - txin.o = Buffer.concat([hash, voutBuf]); - - txobj.ins.push(txin); - }); - - var keys = Object.keys(obj.outputs); - keys.forEach(function(addrStr) { - var addr = new Address(addrStr); - var script = Script.createPubKeyHashOut(addr.payload()); - - var valueNum = bignum(obj.outputs[addrStr]); - var value = util.bigIntToValue(valueNum); - - var txout = new TransactionOut(); - txout.v = value; - txout.s = script.getBuffer(); - - txobj.outs.push(txout); - }); + var tx = new Transaction(txobj); this.lock_time = txobj.lock_time; this.version = txobj.version; @@ -694,7 +667,8 @@ Transaction.prototype.parse = function (parser) { * }, [...] * ] * This is compatible con insight's /utxo API. - * NOTE that amount is in BTCs! (as returned in insight and bitcoind. + * NOTE that amount is in BTCs! (as returned in insight and bitcoind) + * amountSat can be given to provide amount in satochis. * * @totalNeededAmount: output transaction amount in BTC, including fee * @@ -705,17 +679,27 @@ Transaction.prototype.parse = function (parser) { */ Transaction.selectUnspent = function (unspentArray, totalNeededAmount) { + // TODO implement bidcoind heristics + // A- + // 1) select utxos with 6+ confirmations + // 2) if not 2) select utxos with 1+ confirmations + // 3) if not select unconfirmed. + // + // B- + // Select smaller utxos first. + // + // // TODO we could randomize or select the selection var selected = []; var l = unspentArray.length; var totalSat = bignum(0); - var totalNeededAmountSat = bignum(totalNeededAmount * util.COIN); + var totalNeededAmountSat = util.parseValue(totalNeededAmount); var fullfill = false; for(var i = 0; i= 0) { @@ -727,7 +711,132 @@ Transaction.selectUnspent = function (unspentArray, totalNeededAmount) { return selected; } +/* + * _scriptForAddress + * + * Returns a scriptPubKey for the given address type + */ +Transaction._scriptForAddress = function (addressString) { + + var livenet = networks.livenet; + var testnet = networks.testnet; + var address = new Address(addressString); + + var version = address.version(); + var script; + if (version == livenet.addressPubkey || version == testnet.addressPubkey) + script = Script.createPubKeyHashOut(address.payload()); + else if (version == livenet.addressScript || version == testnet.addressScript) + script = Script.createP2SH(address.payload()); + else + throw new Error('invalid output address'); + + return script; +}; + +/* + * create + * + * creates a transaction + * + * @ins + * a selected set of utxos, as described on selectUnspent + * @outs + * an array of [{ + * address: xx, + * amount:0.001 +* },...] + * @opts + * { + * remainderAddress: null, + * fee: 0.001, + * lockTime: null, + * } + * + * Amounts are in BTC. instead of fee and amount; feeSat and amountSat can be given, + * repectively, to provide amounts in satoshis. + * + * (TODO: should we use uBTC already?) + * + * If not remainderAddress is given, and there is a remainderAddress + * first in address will be used. (TODO: is this is reasoable?) + * + * TODO add exceptions for validations: + * out address - ins amount > out amount - fees < maxFees + * + more? + */ + +Transaction.create = function (ins, outs, opts) { + opts = opts || {}; + + var feeSat = opts.feeSat || util.parseValue(opts.fee || DEFAULT_FEE); + var txobj = {}; + txobj.version = 1; + txobj.lock_time = opts.lockTime || 0; + txobj.ins = []; + txobj.outs = []; + + var l = ins.length; + var valueInSat = bignum(0); + for(var i=0; i0) { + var remainderAddress = opts.remainderAddress || ins[0].address; + + outs.push({ + address: remainderAddress, + amountSat: remainderSat, + }); + } + + for(var i=0;i