7
7
8
8
var Q = require ( 'q' ) ;
9
9
var bitcoin = require ( './bitcoin' ) ;
10
+ var bitcoinCash = require ( './bitcoinCash' ) ;
10
11
var common = require ( './common' ) ;
11
12
var Util = require ( './util' ) ;
12
13
var _ = require ( 'lodash' ) ;
13
14
14
15
const P2SH_INPUT_SIZE = 295 ;
16
+ const P2SH_P2WSH_INPUT_SIZE = 139 ;
15
17
const P2PKH_INPUT_SIZE = 160 ;
16
18
const OUTPUT_SIZE = 34 ;
17
19
const TX_OVERHEAD_SIZE = 10 ;
@@ -363,10 +365,14 @@ exports.createTransaction = function(params) {
363
365
unspents = _ . filter ( unspents , function ( unspent ) {
364
366
return unspent . value > minInputValue ;
365
367
} ) ;
368
+ let segwitInputCount = 0 ;
366
369
unspents . every ( function ( unspent ) {
370
+ if ( unspent . witnessScript ) {
371
+ segwitInputCount ++ ;
372
+ }
367
373
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
+
370
376
return ( inputAmount < ( feeSingleKeySourceAddress ? totalOutputAmount : totalAmount ) ) ;
371
377
} ) ;
372
378
@@ -386,7 +392,8 @@ exports.createTransaction = function(params) {
386
392
}
387
393
388
394
txInfo = {
389
- nP2SHInputs : transaction . tx . ins . length - ( feeSingleKeySourceAddress ? 1 : 0 ) ,
395
+ nP2SHInputs : transaction . tx . ins . length - ( feeSingleKeySourceAddress ? 1 : 0 ) - segwitInputCount ,
396
+ nP2SHP2WSHInputs : segwitInputCount ,
390
397
nP2PKHInputs : feeSingleKeySourceAddress ? 1 : 0 ,
391
398
nOutputs : (
392
399
recipients . length + 1 + // recipients and change
@@ -398,6 +405,7 @@ exports.createTransaction = function(params) {
398
405
399
406
estTxSize = estimateTransactionSize ( {
400
407
nP2SHInputs : txInfo . nP2SHInputs ,
408
+ nP2SHP2WSHInputs : txInfo . nP2SHP2WSHInputs ,
401
409
nP2PKHInputs : txInfo . nP2PKHInputs ,
402
410
nOutputs : txInfo . nOutputs
403
411
} ) ;
@@ -407,6 +415,7 @@ exports.createTransaction = function(params) {
407
415
bitgo : params . wallet . bitgo ,
408
416
feeRate : feeRate ,
409
417
nP2SHInputs : txInfo . nP2SHInputs ,
418
+ nP2SHP2WSHInputs : txInfo . nP2SHP2WSHInputs ,
410
419
nP2PKHInputs : txInfo . nP2PKHInputs ,
411
420
nOutputs : txInfo . nOutputs
412
421
} ) ;
@@ -559,7 +568,10 @@ exports.createTransaction = function(params) {
559
568
return params . changeAddress ;
560
569
} else {
561
570
// 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 } )
563
575
. then ( function ( result ) {
564
576
return result . address ;
565
577
} ) ;
@@ -614,7 +626,7 @@ exports.createTransaction = function(params) {
614
626
var serialize = function ( ) {
615
627
// only need to return the unspents that were used and just the chainPath, redeemScript, and instant flag
616
628
var pickedUnspents = _ . map ( unspents , function ( unspent ) {
617
- return _ . pick ( unspent , [ 'chainPath' , 'redeemScript' , 'instant' ] ) ;
629
+ return _ . pick ( unspent , [ 'chainPath' , 'redeemScript' , 'instant' , 'witnessScript' , 'value' ] ) ;
618
630
} ) ;
619
631
var prunedUnspents = _ . slice ( pickedUnspents , 0 , transaction . tx . ins . length - feeSingleKeyUnspentsUsed . length ) ;
620
632
_ . each ( feeSingleKeyUnspentsUsed , function ( feeUnspent ) {
@@ -669,20 +681,30 @@ exports.createTransaction = function(params) {
669
681
* @returns size: estimated size of the transaction in bytes
670
682
*/
671
683
var estimateTransactionSize = function ( params ) {
672
- if ( typeof ( params . nP2SHInputs ) !== 'number' || params . nP2SHInputs < 1 ) {
684
+ if ( ! _ . isInteger ( params . nP2SHInputs ) || params . nP2SHInputs < 0 ) {
673
685
throw new Error ( 'expecting positive nP2SHInputs' ) ;
674
686
}
675
- if ( ( params . nP2PKHInputs ) && ( typeof ( params . nP2PKHInputs ) !== 'number' ) ) {
687
+ if ( ! _ . isInteger ( params . nP2PKHInputs ) || params . nP2PKHInputs < 0 ) {
676
688
throw new Error ( 'expecting positive nP2PKHInputs to be numeric' ) ;
677
689
}
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 ) {
679
697
throw new Error ( 'expecting positive nOutputs' ) ;
680
698
}
681
699
700
+
701
+
682
702
var estimatedSize = P2SH_INPUT_SIZE * params . nP2SHInputs +
703
+ P2SH_P2WSH_INPUT_SIZE * ( params . nP2SHP2WSHInputs || 0 ) +
683
704
P2PKH_INPUT_SIZE * ( params . nP2PKHInputs || 0 ) +
684
705
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 ) ;
686
708
687
709
return estimatedSize ;
688
710
} ;
@@ -736,16 +758,16 @@ exports.signTransaction = function(params) {
736
758
737
759
var validate = ( params . validate === undefined ) ? true : params . validate ;
738
760
var privKey ;
739
- if ( typeof ( params . transactionHex ) != 'string' ) {
761
+ if ( typeof ( params . transactionHex ) !== 'string' ) {
740
762
throw new Error ( 'expecting the transaction hex as a string' ) ;
741
763
}
742
764
if ( ! Array . isArray ( params . unspents ) ) {
743
765
throw new Error ( 'expecting the unspents array' ) ;
744
766
}
745
- if ( typeof ( validate ) != 'boolean' ) {
767
+ if ( typeof ( validate ) !== 'boolean' ) {
746
768
throw new Error ( 'expecting validate to be a boolean' ) ;
747
769
}
748
- if ( typeof ( keychain ) != 'object' || typeof ( keychain . xprv ) != 'string' ) {
770
+ if ( typeof ( keychain ) !== 'object' || typeof ( keychain . xprv ) != = 'string' ) {
749
771
if ( typeof ( params . signingKey ) === 'string' ) {
750
772
privKey = bitcoin . ECPair . fromWIF ( params . signingKey , bitcoin . getNetwork ( ) ) ;
751
773
keychain = undefined ;
@@ -759,7 +781,7 @@ exports.signTransaction = function(params) {
759
781
feeSingleKey = bitcoin . ECPair . fromWIF ( params . feeSingleKeyWIF , bitcoin . getNetwork ( ) ) ;
760
782
}
761
783
762
- var transaction = bitcoin . Transaction . fromHex ( params . transactionHex ) ;
784
+ var transaction = bitcoinCash . Transaction . fromHex ( params . transactionHex ) ;
763
785
if ( transaction . ins . length !== params . unspents . length ) {
764
786
throw new Error ( 'length of unspents array should equal to the number of transaction inputs' ) ;
765
787
}
@@ -769,47 +791,57 @@ exports.signTransaction = function(params) {
769
791
var rootExtKey = bitcoin . HDNode . fromBase58 ( keychain . xprv ) ;
770
792
hdPath = bitcoin . hdPath ( rootExtKey ) ;
771
793
}
772
- var txb ;
773
794
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 ) {
776
800
// this is the input from a single key fee address
777
801
if ( ! feeSingleKey ) {
778
802
throw new Error ( 'single key address used in input but feeSingleKeyWIF not provided' ) ;
779
803
}
780
804
781
- txb = bitcoin . TransactionBuilder . fromTransaction ( transaction ) ;
782
805
txb . sign ( index , feeSingleKey ) ;
783
- transaction = txb . buildIncomplete ( ) ;
784
806
continue ;
785
807
}
786
808
809
+ const chainPath = currentUnspent . chainPath ;
787
810
if ( hdPath ) {
788
811
var subPath = keychain . walletSubPath || '/0/0' ;
789
- var path = keychain . path + subPath + params . unspents [ index ] . chainPath ;
812
+ var path = keychain . path + subPath + chainPath ;
790
813
privKey = hdPath . deriveKey ( path ) ;
791
814
}
792
815
816
+ const isSegwitInput = ! ! currentUnspent . witnessScript ;
817
+
793
818
// subscript is the part of the output script after the OP_CODESEPARATOR.
794
819
// Since we are only ever signing p2sh outputs, which do not have
795
820
// 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 ;
797
823
798
824
// In order to sign with bitcoinjs-lib, we must use its transaction
799
825
// builder, confusingly named the same exact thing as our transaction
800
826
// builder, but with inequivalent behavior.
801
- txb = bitcoin . TransactionBuilder . fromTransaction ( transaction ) ;
802
827
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
+ }
804
835
} catch ( e ) {
805
836
return Q . reject ( 'Failed to sign input #' + index ) ;
806
837
}
807
838
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 ( ) ;
812
843
844
+ for ( let index = 0 ; index < transaction . ins . length ; ++ index ) {
813
845
// bitcoinjs-lib adds one more OP_0 than we need. It creates one OP_0 for
814
846
// every n public keys in an m-of-n multisig, and replaces the OP_0s with
815
847
// the signature of the nth public key, then removes any remaining OP_0s
@@ -819,30 +851,21 @@ exports.signTransaction = function(params) {
819
851
// chronological order, but is not compatible with the BitGo API, which
820
852
// assumes m OP_0s for m-of-n multisig (or m-1 after the first signature
821
853
// 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
- }
831
854
832
- transaction . ins [ index ] . script = bitcoin . script . compile ( chunks ) ;
855
+ const currentUnspent = params . unspents [ index ] ;
833
856
834
857
// The signatures are validated server side and on the bitcoin network, so
835
858
// the signature validation is optional and can be disabled by setting:
836
859
// validate = false
837
860
if ( validate ) {
838
- if ( exports . verifyInputSignatures ( transaction , index , subscript ) !== - 1 ) {
861
+ if ( exports . verifyInputSignatures ( transaction , index , currentUnspent . validationScript , false , currentUnspent . value ) !== - 1 ) {
839
862
throw new Error ( 'number of signatures is invalid - something went wrong when signing' ) ;
840
863
}
841
864
}
842
865
}
843
866
844
867
return Q . when ( {
845
- transactionHex : transaction . toBuffer ( ) . toString ( 'hex' )
868
+ transactionHex : transaction . toHex ( )
846
869
} ) ;
847
870
} ;
848
871
@@ -855,22 +878,34 @@ exports.signTransaction = function(params) {
855
878
* @param inputIndex the input index to verify
856
879
* @param pubScript the redeem script to verify with
857
880
* @param ignoreKeyIndices array of multisig keys indexes (in order of keychains on the wallet). e.g. [1] to ignore backup keys
881
+ * @param amount
858
882
* @returns {number }
859
883
*/
860
- exports . verifyInputSignatures = function ( transaction , inputIndex , pubScript , ignoreKeyIndices ) {
884
+ exports . verifyInputSignatures = function ( transaction , inputIndex , pubScript , ignoreKeyIndices , amount ) {
861
885
if ( inputIndex < 0 || inputIndex >= transaction . ins . length ) {
862
886
throw new Error ( 'illegal index' ) ;
863
887
}
864
888
865
889
ignoreKeyIndices = ignoreKeyIndices || [ ] ;
866
- var sigScript = transaction . ins [ inputIndex ] . script ;
890
+ const currentTransactionInput = transaction . ins [ inputIndex ] ;
891
+ var sigScript = currentTransactionInput . script ;
867
892
var sigsNeeded = 1 ;
868
893
var sigs = [ ] ;
869
894
var pubKeys = [ ] ;
870
895
var decompiledSigScript = bitcoin . script . decompile ( sigScript ) ;
871
896
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
+
872
906
// 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 ) {
874
909
case 'scripthash' :
875
910
// Replace the pubScript with the P2SH Script.
876
911
pubScript = decompiledSigScript [ decompiledSigScript . length - 1 ] ;
@@ -897,16 +932,21 @@ exports.verifyInputSignatures = function(transaction, inputIndex, pubScript, ign
897
932
return 0 ;
898
933
}
899
934
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 ) {
902
937
// 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 ) {
904
939
continue ;
905
940
}
906
941
907
942
var hashType = sigs [ sigIndex ] [ sigs [ sigIndex ] . length - 1 ] ;
908
943
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
+ }
910
950
911
951
var validSig = false ;
912
952
0 commit comments