@ -2,7 +2,6 @@ var BigInteger = require('./jsbn/jsbn');
var Script = require ( './script' ) ;
var util = require ( './util' ) ;
var convert = require ( './convert' ) ;
var Wallet = require ( './wallet' ) ;
var ECKey = require ( './eckey' ) . ECKey ;
var ECDSA = require ( './ecdsa' ) ;
var Address = require ( './address' ) ;
@ -15,8 +14,7 @@ var Transaction = function (doc) {
this . locktime = 0 ;
this . ins = [ ] ;
this . outs = [ ] ;
this . timestamp = null ;
this . block = null ;
this . defaultSequence = [ 255 , 255 , 255 , 255 ] // 0xFFFFFFFF
if ( doc ) {
if ( typeof doc == "string" || Array . isArray ( doc ) ) {
@ -35,27 +33,11 @@ var Transaction = function (doc) {
this . addOutput ( new TransactionOut ( doc . outs [ i ] ) ) ;
}
}
if ( doc . timestamp ) this . timestamp = doc . timestamp ;
if ( doc . block ) this . block = doc . block ;
this . hash = this . hash || this . getHash ( )
}
} ;
/ * *
* Turn transaction data into Transaction objects .
*
* Takes an array of plain JavaScript objects containing transaction data and
* returns an array of Transaction objects .
* /
Transaction . objectify = function ( txs ) {
var objs = [ ] ;
for ( var i = 0 ; i < txs . length ; i ++ ) {
objs . push ( new Transaction ( txs [ i ] ) ) ;
}
return objs ;
} ;
/ * *
* Create a new txin .
*
@ -85,7 +67,7 @@ Transaction.prototype.addInput = function (tx, outIndex) {
index : outIndex
} ,
script : new Script ( ) ,
sequence : 4294967295
sequence : this . defaultSequence
} ) ) ;
}
} ;
@ -138,7 +120,7 @@ Transaction.prototype.serialize = function () {
var scriptBytes = txin . script . buffer ;
buffer = buffer . concat ( convert . numToVarInt ( scriptBytes . length ) ) ;
buffer = buffer . concat ( scriptBytes ) ;
buffer = buffer . concat ( convert . numToBytes ( parseInt ( txin . sequence ) , 4 ) ) ;
buffer = buffer . concat ( txin . sequence ) ;
}
buffer = buffer . concat ( convert . numToVarInt ( this . outs . length ) ) ;
for ( var i = 0 ; i < this . outs . length ; i ++ ) {
@ -191,23 +173,6 @@ function (connectedScript, inIndex, hashType)
txTmp . ins [ inIndex ] . script = connectedScript ;
// Blank out some of the outputs
if ( ( hashType & 0x1f ) == SIGHASH_NONE ) {
txTmp . outs = [ ] ;
// Let the others update at will
for ( var i = 0 ; i < txTmp . ins . length ; i ++ )
if ( i != inIndex )
txTmp . ins [ i ] . sequence = 0 ;
} else if ( ( hashType & 0x1f ) == SIGHASH_SINGLE ) {
// TODO: Implement
}
// Blank out other inputs completely, not recommended for open transactions
if ( hashType & SIGHASH_ANYONECANPAY ) {
txTmp . ins = [ txTmp . ins [ inIndex ] ] ;
}
var buffer = txTmp . serialize ( ) ;
buffer = buffer . concat ( convert . numToBytes ( parseInt ( hashType ) , 4 ) ) ;
@ -246,169 +211,6 @@ Transaction.prototype.clone = function ()
return newTx ;
} ;
/ * *
* Analyze how this transaction affects a wallet .
*
* Returns an object with properties 'impact' , 'type' and 'addr' .
*
* 'impact' is an object , see Transaction # calcImpact .
*
* 'type' can be one of the following :
*
* recv :
* This is an incoming transaction , the wallet received money .
* 'addr' contains the first address in the wallet that receives money
* from this transaction .
*
* self :
* This is an internal transaction , money was sent within the wallet .
* 'addr' is undefined .
*
* sent :
* This is an outgoing transaction , money was sent out from the wallet .
* 'addr' contains the first external address , i . e . the recipient .
*
* other :
* This method was unable to detect what the transaction does . Either it
* /
Transaction . prototype . analyze = function ( wallet ) {
if ( ! ( wallet instanceof Wallet ) ) return null ;
var allFromMe = true ,
allToMe = true ,
firstRecvHash = null ,
firstMeRecvHash = null ,
firstSendHash = null ;
for ( var i = this . outs . length - 1 ; i >= 0 ; i -- ) {
var txout = this . outs [ i ] ;
var hash = txout . script . simpleOutPubKeyHash ( ) ;
if ( ! wallet . hasHash ( hash ) ) {
allToMe = false ;
} else {
firstMeRecvHash = hash ;
}
firstRecvHash = hash ;
}
for ( var i = this . ins . length - 1 ; i >= 0 ; i -- ) {
var txin = this . ins [ i ] ;
firstSendHash = txin . script . simpleInPubKeyHash ( ) ;
if ( ! wallet . hasHash ( firstSendHash ) ) {
allFromMe = false ;
break ;
}
}
var impact = this . calcImpact ( wallet ) ;
var analysis = { } ;
analysis . impact = impact ;
if ( impact . sign > 0 && impact . value > 0 ) {
analysis . type = 'recv' ;
analysis . addr = new Address ( firstMeRecvHash ) ;
} else if ( allFromMe && allToMe ) {
analysis . type = 'self' ;
} else if ( allFromMe ) {
analysis . type = 'sent' ;
// TODO: Right now, firstRecvHash is the first output, which - if the
// transaction was not generated by this library could be the
// change address.
analysis . addr = new Address ( firstRecvHash ) ;
} else {
analysis . type = "other" ;
}
return analysis ;
} ;
/ * *
* Get a human - readable version of the data returned by Transaction # analyze .
*
* This is merely a convenience function . Clients should consider implementing
* this themselves based on their UI , I18N , etc .
* /
Transaction . prototype . getDescription = function ( wallet ) {
var analysis = this . analyze ( wallet ) ;
if ( ! analysis ) return "" ;
switch ( analysis . type ) {
case 'recv' :
return "Received with " + analysis . addr ;
break ;
case 'sent' :
return "Payment to " + analysis . addr ;
break ;
case 'self' :
return "Payment to yourself" ;
break ;
case 'other' :
default :
return "" ;
}
} ;
/ * *
* Get the total amount of a transaction ' s outputs .
* /
Transaction . prototype . getTotalOutValue = function ( ) {
return this . outs . reduce ( function ( t , o ) { return t + o . value } , 0 ) ;
} ;
/ * *
* Old name for Transaction # getTotalOutValue .
*
* @ deprecated
* /
Transaction . prototype . getTotalValue = Transaction . prototype . getTotalOutValue ;
/ * *
* Calculates the impact a transaction has on this wallet .
*
* Based on the its public keys , the wallet will calculate the
* credit or debit of this transaction .
*
* It will return an object with two properties :
* - sign : 1 or - 1 depending on sign of the calculated impact .
* - value : amount of calculated impact
*
* @ returns Object Impact on wallet
* /
Transaction . prototype . calcImpact = function ( wallet ) {
if ( ! ( wallet instanceof Wallet ) ) return 0 ;
// Calculate credit to us from all outputs
var valueOut = this . outs . filter ( function ( o ) {
return wallet . hasHash ( convert . bytesToHex ( o . script . simpleOutPubKeyHash ( ) ) ) ;
} )
. reduce ( function ( t , o ) { return t + o . value } , 0 ) ;
var valueIn = this . ins . filter ( function ( i ) {
return wallet . hasHash ( convert . bytesToHex ( i . script . simpleInPubKeyHash ( ) ) )
&& wallet . txIndex [ i . outpoint . hash ] ;
} )
. reduce ( function ( t , i ) {
return t + wallet . txIndex [ i . outpoint . hash ] . outs [ i . outpoint . index ] . value
} , 0 ) ;
if ( valueOut > valueIn ) {
return {
sign : 1 ,
value : valueOut - valueIn
} ;
} else {
return {
sign : - 1 ,
value : valueIn - valueOut
} ;
}
} ;
/ * *
* Converts a serialized transaction into a transaction object
* /
@ -451,7 +253,7 @@ Transaction.deserialize = function(buffer) {
index : readAsInt ( 4 )
} ,
script : new Script ( readVarString ( ) ) ,
sequence : readAsInt ( 4 )
sequence : readBytes ( 4 )
} ) ;
}
var outs = readVarInt ( ) ;
@ -473,6 +275,9 @@ Transaction.deserialize = function(buffer) {
Transaction . prototype . sign = function ( index , key , type ) {
type = type || SIGHASH_ALL ;
key = new ECKey ( key ) ;
// TODO: getPub is slow, sha256ripe160 probably is too.
// This could be sped up a lot by providing these as inputs.
var pub = key . getPub ( ) . export ( 'bytes' ) ,
hash160 = util . sha256ripe160 ( pub ) ,
script = Script . createOutputScript ( new Address ( hash160 ) ) ,
@ -533,7 +338,6 @@ Transaction.prototype.validateSig = function(index, script, sig, pub) {
convert . coerceToBytes ( pub ) ) ;
}
var TransactionIn = function ( data ) {
if ( typeof data == "string" )
this . outpoint = { hash : data . split ( ':' ) [ 0 ] , index : data . split ( ':' ) [ 1 ] }
@ -549,7 +353,7 @@ var TransactionIn = function (data) {
else
this . script = new Script ( data . script )
this . sequence = data . sequence || 4294967295 ;
this . sequence = data . sequence || this . defaultSequence
} ;
TransactionIn . prototype . clone = function ( ) {