77
88var Q = require ( 'q' ) ;
99var bitcoin = require ( './bitcoin' ) ;
10+ var bitcoinCash = require ( './bitcoinCash' ) ;
1011var common = require ( './common' ) ;
1112var Util = require ( './util' ) ;
1213var _ = require ( 'lodash' ) ;
1314
1415const P2SH_INPUT_SIZE = 295 ;
16+ const P2SH_P2WSH_INPUT_SIZE = 139 ;
1517const P2PKH_INPUT_SIZE = 160 ;
1618const OUTPUT_SIZE = 34 ;
1719const TX_OVERHEAD_SIZE = 10 ;
@@ -363,10 +365,14 @@ exports.createTransaction = function(params) {
363365 unspents = _ . filter ( unspents , function ( unspent ) {
364366 return unspent . value > minInputValue ;
365367 } ) ;
368+ let segwitInputCount = 0 ;
366369 unspents . every ( function ( unspent ) {
370+ if ( unspent . witnessScript ) {
371+ segwitInputCount ++ ;
372+ }
367373 inputAmount += unspent . value ;
368- var script = new Buffer ( unspent . script , 'hex' ) ;
369- transaction . addInput ( unspent . tx_hash , unspent . tx_output_n , 0xffffffff , script ) ;
374+ transaction . addInput ( unspent . tx_hash , unspent . tx_output_n , 0xffffffff ) ;
375+
370376 return ( inputAmount < ( feeSingleKeySourceAddress ? totalOutputAmount : totalAmount ) ) ;
371377 } ) ;
372378
@@ -386,7 +392,8 @@ exports.createTransaction = function(params) {
386392 }
387393
388394 txInfo = {
389- nP2SHInputs : transaction . tx . ins . length - ( feeSingleKeySourceAddress ? 1 : 0 ) ,
395+ nP2SHInputs : transaction . tx . ins . length - ( feeSingleKeySourceAddress ? 1 : 0 ) - segwitInputCount ,
396+ nP2SHP2WSHInputs : segwitInputCount ,
390397 nP2PKHInputs : feeSingleKeySourceAddress ? 1 : 0 ,
391398 nOutputs : (
392399 recipients . length + 1 + // recipients and change
@@ -398,6 +405,7 @@ exports.createTransaction = function(params) {
398405
399406 estTxSize = estimateTransactionSize ( {
400407 nP2SHInputs : txInfo . nP2SHInputs ,
408+ nP2SHP2WSHInputs : txInfo . nP2SHP2WSHInputs ,
401409 nP2PKHInputs : txInfo . nP2PKHInputs ,
402410 nOutputs : txInfo . nOutputs
403411 } ) ;
@@ -407,6 +415,7 @@ exports.createTransaction = function(params) {
407415 bitgo : params . wallet . bitgo ,
408416 feeRate : feeRate ,
409417 nP2SHInputs : txInfo . nP2SHInputs ,
418+ nP2SHP2WSHInputs : txInfo . nP2SHP2WSHInputs ,
410419 nP2PKHInputs : txInfo . nP2PKHInputs ,
411420 nOutputs : txInfo . nOutputs
412421 } ) ;
@@ -559,7 +568,10 @@ exports.createTransaction = function(params) {
559568 return params . changeAddress ;
560569 } else {
561570 // Otherwise create a new address per output, for privacy
562- return params . wallet . createAddress ( { chain : 1 , validate : validate } )
571+ // determine if segwit or not
572+ const isSegwit = bitgo . getConstants ( ) . enableSegwit ;
573+ const changeChain = isSegwit ? 11 : 1 ;
574+ return params . wallet . createAddress ( { chain : changeChain , validate : validate } )
563575 . then ( function ( result ) {
564576 return result . address ;
565577 } ) ;
@@ -614,7 +626,7 @@ exports.createTransaction = function(params) {
614626 var serialize = function ( ) {
615627 // only need to return the unspents that were used and just the chainPath, redeemScript, and instant flag
616628 var pickedUnspents = _ . map ( unspents , function ( unspent ) {
617- return _ . pick ( unspent , [ 'chainPath' , 'redeemScript' , 'instant' ] ) ;
629+ return _ . pick ( unspent , [ 'chainPath' , 'redeemScript' , 'instant' , 'witnessScript' , 'value' ] ) ;
618630 } ) ;
619631 var prunedUnspents = _ . slice ( pickedUnspents , 0 , transaction . tx . ins . length - feeSingleKeyUnspentsUsed . length ) ;
620632 _ . each ( feeSingleKeyUnspentsUsed , function ( feeUnspent ) {
@@ -669,20 +681,30 @@ exports.createTransaction = function(params) {
669681 * @returns size: estimated size of the transaction in bytes
670682 */
671683var estimateTransactionSize = function ( params ) {
672- if ( typeof ( params . nP2SHInputs ) !== 'number' || params . nP2SHInputs < 1 ) {
684+ if ( ! _ . isInteger ( params . nP2SHInputs ) || params . nP2SHInputs < 0 ) {
673685 throw new Error ( 'expecting positive nP2SHInputs' ) ;
674686 }
675- if ( ( params . nP2PKHInputs ) && ( typeof ( params . nP2PKHInputs ) !== 'number' ) ) {
687+ if ( ! _ . isInteger ( params . nP2PKHInputs ) || params . nP2PKHInputs < 0 ) {
676688 throw new Error ( 'expecting positive nP2PKHInputs to be numeric' ) ;
677689 }
678- if ( typeof ( params . nOutputs ) !== 'number' || params . nOutputs < 1 ) {
690+ if ( ! _ . isInteger ( params . nP2SHP2WSHInputs ) || params . nP2SHP2WSHInputs < 0 ) {
691+ throw new Error ( 'expecting positive nP2SHP2WSHInputs to be numeric' ) ;
692+ }
693+ if ( ( params . nP2SHInputs + params . nP2SHP2WSHInputs ) < 1 ) {
694+ throw new Error ( 'expecting at least one nP2SHInputs or nP2SHP2WSHInputs' ) ;
695+ }
696+ if ( ! _ . isInteger ( params . nOutputs ) || params . nOutputs < 1 ) {
679697 throw new Error ( 'expecting positive nOutputs' ) ;
680698 }
681699
700+
701+
682702 var estimatedSize = P2SH_INPUT_SIZE * params . nP2SHInputs +
703+ P2SH_P2WSH_INPUT_SIZE * ( params . nP2SHP2WSHInputs || 0 ) +
683704 P2PKH_INPUT_SIZE * ( params . nP2PKHInputs || 0 ) +
684705 OUTPUT_SIZE * params . nOutputs +
685- TX_OVERHEAD_SIZE ;
706+ // if the tx contains at least one segwit input, the tx overhead is increased by 1
707+ TX_OVERHEAD_SIZE + ( params . nP2SHP2WSHInputs > 0 ? 1 : 0 ) ;
686708
687709 return estimatedSize ;
688710} ;
@@ -736,16 +758,16 @@ exports.signTransaction = function(params) {
736758
737759 var validate = ( params . validate === undefined ) ? true : params . validate ;
738760 var privKey ;
739- if ( typeof ( params . transactionHex ) != 'string' ) {
761+ if ( typeof ( params . transactionHex ) !== 'string' ) {
740762 throw new Error ( 'expecting the transaction hex as a string' ) ;
741763 }
742764 if ( ! Array . isArray ( params . unspents ) ) {
743765 throw new Error ( 'expecting the unspents array' ) ;
744766 }
745- if ( typeof ( validate ) != 'boolean' ) {
767+ if ( typeof ( validate ) !== 'boolean' ) {
746768 throw new Error ( 'expecting validate to be a boolean' ) ;
747769 }
748- if ( typeof ( keychain ) != 'object' || typeof ( keychain . xprv ) != 'string' ) {
770+ if ( typeof ( keychain ) !== 'object' || typeof ( keychain . xprv ) != = 'string' ) {
749771 if ( typeof ( params . signingKey ) === 'string' ) {
750772 privKey = bitcoin . ECPair . fromWIF ( params . signingKey , bitcoin . getNetwork ( ) ) ;
751773 keychain = undefined ;
@@ -759,7 +781,7 @@ exports.signTransaction = function(params) {
759781 feeSingleKey = bitcoin . ECPair . fromWIF ( params . feeSingleKeyWIF , bitcoin . getNetwork ( ) ) ;
760782 }
761783
762- var transaction = bitcoin . Transaction . fromHex ( params . transactionHex ) ;
784+ var transaction = bitcoinCash . Transaction . fromHex ( params . transactionHex ) ;
763785 if ( transaction . ins . length !== params . unspents . length ) {
764786 throw new Error ( 'length of unspents array should equal to the number of transaction inputs' ) ;
765787 }
@@ -769,47 +791,57 @@ exports.signTransaction = function(params) {
769791 var rootExtKey = bitcoin . HDNode . fromBase58 ( keychain . xprv ) ;
770792 hdPath = bitcoin . hdPath ( rootExtKey ) ;
771793 }
772- var txb ;
773794
774- for ( var index = 0 ; index < transaction . ins . length ; ++ index ) {
775- if ( params . unspents [ index ] . redeemScript === false ) {
795+ var txb = bitcoinCash . TransactionBuilder . fromTransaction ( transaction , rootExtKey . keyPair . network ) ;
796+
797+ for ( let index = 0 ; index < txb . tx . ins . length ; ++ index ) {
798+ const currentUnspent = params . unspents [ index ] ;
799+ if ( currentUnspent . redeemScript === false ) {
776800 // this is the input from a single key fee address
777801 if ( ! feeSingleKey ) {
778802 throw new Error ( 'single key address used in input but feeSingleKeyWIF not provided' ) ;
779803 }
780804
781- txb = bitcoin . TransactionBuilder . fromTransaction ( transaction ) ;
782805 txb . sign ( index , feeSingleKey ) ;
783- transaction = txb . buildIncomplete ( ) ;
784806 continue ;
785807 }
786808
809+ const chainPath = currentUnspent . chainPath ;
787810 if ( hdPath ) {
788811 var subPath = keychain . walletSubPath || '/0/0' ;
789- var path = keychain . path + subPath + params . unspents [ index ] . chainPath ;
812+ var path = keychain . path + subPath + chainPath ;
790813 privKey = hdPath . deriveKey ( path ) ;
791814 }
792815
816+ const isSegwitInput = ! ! currentUnspent . witnessScript ;
817+
793818 // subscript is the part of the output script after the OP_CODESEPARATOR.
794819 // Since we are only ever signing p2sh outputs, which do not have
795820 // OP_CODESEPARATORS, it is always the output script.
796- var subscript = new Buffer ( params . unspents [ index ] . redeemScript , 'hex' ) ;
821+ let subscript = new Buffer ( currentUnspent . redeemScript , 'hex' ) ;
822+ currentUnspent . validationScript = subscript ;
797823
798824 // In order to sign with bitcoinjs-lib, we must use its transaction
799825 // builder, confusingly named the same exact thing as our transaction
800826 // builder, but with inequivalent behavior.
801- txb = bitcoin . TransactionBuilder . fromTransaction ( transaction ) ;
802827 try {
803- txb . sign ( index , privKey , subscript , bitcoin . Transaction . SIGHASH_ALL ) ;
828+ if ( isSegwitInput ) {
829+ const witnessScript = new Buffer ( currentUnspent . witnessScript , 'hex' ) ;
830+ currentUnspent . validationScript = witnessScript ;
831+ txb . sign ( index , privKey , subscript , bitcoin . Transaction . SIGHASH_ALL , currentUnspent . value , witnessScript ) ;
832+ } else {
833+ txb . sign ( index , privKey , subscript , bitcoin . Transaction . SIGHASH_ALL ) ;
834+ }
804835 } catch ( e ) {
805836 return Q . reject ( 'Failed to sign input #' + index ) ;
806837 }
807838
808- // Build the "incomplete" transaction, i.e. one that does not have all
809- // the signatures (since we are only signing the first of 2 signatures in
810- // a 2-of-3 multisig).
811- transaction = txb . buildIncomplete ( ) ;
839+ }
840+
841+ // reserialize transaction
842+ transaction = txb . build ( ) ;
812843
844+ for ( let index = 0 ; index < transaction . ins . length ; ++ index ) {
813845 // bitcoinjs-lib adds one more OP_0 than we need. It creates one OP_0 for
814846 // every n public keys in an m-of-n multisig, and replaces the OP_0s with
815847 // the signature of the nth public key, then removes any remaining OP_0s
@@ -819,30 +851,21 @@ exports.signTransaction = function(params) {
819851 // chronological order, but is not compatible with the BitGo API, which
820852 // assumes m OP_0s for m-of-n multisig (or m-1 after the first signature
821853 // is created). Thus we need to remove the superfluous OP_0.
822- var chunks = bitcoin . script . decompile ( transaction . ins [ index ] . script ) ;
823- if ( chunks . length !== 5 ) {
824- throw new Error ( 'unexpected number of chunks in the OP_CHECKMULTISIG script after signing' ) ;
825- }
826- if ( chunks [ 1 ] ) {
827- chunks . splice ( 2 , 1 ) ; // The extra OP_0 is the third chunk
828- } else if ( chunks [ 2 ] ) {
829- chunks . splice ( 1 , 1 ) ; // The extra OP_0 is the second chunk
830- }
831854
832- transaction . ins [ index ] . script = bitcoin . script . compile ( chunks ) ;
855+ const currentUnspent = params . unspents [ index ] ;
833856
834857 // The signatures are validated server side and on the bitcoin network, so
835858 // the signature validation is optional and can be disabled by setting:
836859 // validate = false
837860 if ( validate ) {
838- if ( exports . verifyInputSignatures ( transaction , index , subscript ) !== - 1 ) {
861+ if ( exports . verifyInputSignatures ( transaction , index , currentUnspent . validationScript , false , currentUnspent . value ) !== - 1 ) {
839862 throw new Error ( 'number of signatures is invalid - something went wrong when signing' ) ;
840863 }
841864 }
842865 }
843866
844867 return Q . when ( {
845- transactionHex : transaction . toBuffer ( ) . toString ( 'hex' )
868+ transactionHex : transaction . toHex ( )
846869 } ) ;
847870} ;
848871
@@ -855,22 +878,34 @@ exports.signTransaction = function(params) {
855878 * @param inputIndex the input index to verify
856879 * @param pubScript the redeem script to verify with
857880 * @param ignoreKeyIndices array of multisig keys indexes (in order of keychains on the wallet). e.g. [1] to ignore backup keys
881+ * @param amount
858882 * @returns {number }
859883 */
860- exports . verifyInputSignatures = function ( transaction , inputIndex , pubScript , ignoreKeyIndices ) {
884+ exports . verifyInputSignatures = function ( transaction , inputIndex , pubScript , ignoreKeyIndices , amount ) {
861885 if ( inputIndex < 0 || inputIndex >= transaction . ins . length ) {
862886 throw new Error ( 'illegal index' ) ;
863887 }
864888
865889 ignoreKeyIndices = ignoreKeyIndices || [ ] ;
866- var sigScript = transaction . ins [ inputIndex ] . script ;
890+ const currentTransactionInput = transaction . ins [ inputIndex ] ;
891+ var sigScript = currentTransactionInput . script ;
867892 var sigsNeeded = 1 ;
868893 var sigs = [ ] ;
869894 var pubKeys = [ ] ;
870895 var decompiledSigScript = bitcoin . script . decompile ( sigScript ) ;
871896
897+ const isSegwitInput = currentTransactionInput . witness . length > 0 ;
898+ if ( isSegwitInput ) {
899+ decompiledSigScript = currentTransactionInput . witness ;
900+ sigScript = bitcoin . script . compile ( decompiledSigScript ) ;
901+ if ( ! amount ) {
902+ return 0 ;
903+ }
904+ }
905+
872906 // Check the script type to determine number of signatures, the pub keys, and the script to hash.
873- switch ( bitcoin . script . classifyInput ( sigScript , true ) ) {
907+ const inputClassification = bitcoinCash . script . classifyInput ( sigScript , true ) ;
908+ switch ( inputClassification ) {
874909 case 'scripthash' :
875910 // Replace the pubScript with the P2SH Script.
876911 pubScript = decompiledSigScript [ decompiledSigScript . length - 1 ] ;
@@ -897,16 +932,21 @@ exports.verifyInputSignatures = function(transaction, inputIndex, pubScript, ign
897932 return 0 ;
898933 }
899934
900- var numVerifiedSignatures = 0 ;
901- for ( var sigIndex = 0 ; sigIndex < sigs . length ; ++ sigIndex ) {
935+ let numVerifiedSignatures = 0 ;
936+ for ( let sigIndex = 0 ; sigIndex < sigs . length ; ++ sigIndex ) {
902937 // If this is an OP_0, then its been left as a placeholder for a future sig.
903- if ( sigs [ sigIndex ] == bitcoin . opcodes . OP_0 ) {
938+ if ( sigs [ sigIndex ] === bitcoin . opcodes . OP_0 ) {
904939 continue ;
905940 }
906941
907942 var hashType = sigs [ sigIndex ] [ sigs [ sigIndex ] . length - 1 ] ;
908943 sigs [ sigIndex ] = sigs [ sigIndex ] . slice ( 0 , sigs [ sigIndex ] . length - 1 ) ; // pop hash type from end
909- var signatureHash = transaction . hashForSignature ( inputIndex , pubScript , hashType ) ;
944+ let signatureHash ;
945+ if ( isSegwitInput ) {
946+ signatureHash = transaction . hashForWitnessV0 ( inputIndex , pubScript , amount , hashType ) ;
947+ } else {
948+ signatureHash = transaction . hashForSignature ( inputIndex , pubScript , hashType ) ;
949+ }
910950
911951 var validSig = false ;
912952
0 commit comments