@@ -8,17 +8,17 @@ import debugLib from 'debug';
88import BigNumber from 'bignumber.js' ;
99
1010import {
11+ backupKeyRecovery ,
1112 CrossChainRecoverySigned ,
1213 CrossChainRecoveryUnsigned ,
1314 forCoin ,
1415 recoverCrossChain ,
15- RecoveryProvider ,
16- backupKeyRecovery ,
1716 RecoverParams ,
18- V1RecoverParams ,
17+ RecoveryProvider ,
1918 v1BackupKeyRecovery ,
20- V1SweepParams ,
19+ V1RecoverParams ,
2120 v1Sweep ,
21+ V1SweepParams ,
2222} from './recovery' ;
2323
2424import {
@@ -44,19 +44,16 @@ import {
4444 P2trMusig2UnsupportedError ,
4545 P2trUnsupportedError ,
4646 P2wshUnsupportedError ,
47- ParsedTransaction as BaseParsedTransaction ,
4847 ParseTransactionOptions as BaseParseTransactionOptions ,
4948 PrecreateBitGoOptions ,
5049 PresignTransactionOptions ,
51- promiseProps ,
5250 RequestTracer ,
5351 sanitizeLegacyPath ,
5452 SignedTransaction ,
5553 SignTransactionOptions as BaseSignTransactionOptions ,
5654 SupplementGenerateWalletOptions ,
5755 TransactionParams as BaseTransactionParams ,
5856 TransactionPrebuild as BaseTransactionPrebuild ,
59- TransactionRecipient ,
6057 Triple ,
6158 UnexpectedAddressError ,
6259 UnsupportedAddressTypeError ,
@@ -66,11 +63,6 @@ import {
6663 Wallet ,
6764 WalletData ,
6865} from '@bitgo/sdk-core' ;
69- import { CustomChangeOptions , parseOutput } from './parseOutput' ;
70-
71- const debug = debugLib ( 'bitgo:v2:utxo' ) ;
72-
73- import ScriptType2Of3 = utxolib . bitgo . outputScripts . ScriptType2Of3 ;
7466import { isReplayProtectionUnspent } from './replayProtection' ;
7567import { signAndVerifyPsbt , signAndVerifyWalletTransaction } from './sign' ;
7668import { supportedCrossChainRecoveries } from './config' ;
@@ -82,9 +74,15 @@ import {
8274 getTxInputs ,
8375 isScriptRecipient ,
8476} from './transaction' ;
85- import { assertDescriptorWalletAddress } from './descriptor/assertDescriptorWalletAddress ' ;
77+ import { assertDescriptorWalletAddress } from './descriptor' ;
8678
8779import { getChainFromNetwork , getFamilyFromNetwork , getFullNameFromNetwork } from './names' ;
80+ import { CustomChangeOptions , parseTransaction } from './transaction/fixedScript' ;
81+ import { NamedKeychains } from './keychains' ;
82+
83+ const debug = debugLib ( 'bitgo:v2:utxo' ) ;
84+
85+ import ScriptType2Of3 = utxolib . bitgo . outputScripts . ScriptType2Of3 ;
8886
8987type UtxoCustomSigningFunction < TNumber extends number | bigint > = {
9088 ( params : {
@@ -221,12 +219,8 @@ export interface ParseTransactionOptions<TNumber extends number | bigint = numbe
221219 reqId ?: IRequestTracer ;
222220}
223221
224- export interface ParsedTransaction < TNumber extends number | bigint = number > extends BaseParsedTransaction {
225- keychains : {
226- user ?: Keychain ;
227- backup ?: Keychain ;
228- bitgo ?: Keychain ;
229- } ;
222+ export type ParsedTransaction < TNumber extends number | bigint = number > = {
223+ keychains : NamedKeychains ;
230224 keySignatures : {
231225 backupPub ?: string ;
232226 bitgoPub ?: string ;
@@ -240,7 +234,7 @@ export interface ParsedTransaction<TNumber extends number | bigint = number> ext
240234 implicitExternalSpendAmount : TNumber ;
241235 needsCustomChangeKeySignatureVerification : boolean ;
242236 customChange ?: CustomChangeOptions ;
243- }
237+ } ;
244238
245239export interface GenerateAddressOptions {
246240 addressType ?: ScriptType2Of3 ;
@@ -532,6 +526,7 @@ export abstract class AbstractUtxoCoin extends BaseCoin {
532526 }
533527
534528 /**
529+ <<<<<<< HEAD
535530 * @param first
536531 * @param second
537532 * @returns {Array } All outputs that are in the first array but not in the second
@@ -551,6 +546,8 @@ export abstract class AbstractUtxoCoin extends BaseCoin {
551546 }
552547
553548 /**
549+ =======
550+ >>>>>>> 8cc76f6e8 (refactor(abstract-utxo): factor fixedScript parseTransaction)
554551 * Determine an address' type based on its witness and redeem script presence
555552 * @param addressDetails
556553 */
@@ -607,196 +604,7 @@ export abstract class AbstractUtxoCoin extends BaseCoin {
607604 async parseTransaction < TNumber extends number | bigint = number > (
608605 params : ParseTransactionOptions < TNumber >
609606 ) : Promise < ParsedTransaction < TNumber > > {
610- const { txParams, txPrebuild, wallet, verification = { } , reqId } = params ;
611-
612- if ( ! _ . isUndefined ( verification . disableNetworking ) && ! _ . isBoolean ( verification . disableNetworking ) ) {
613- throw new Error ( 'verification.disableNetworking must be a boolean' ) ;
614- }
615- const disableNetworking = verification . disableNetworking ;
616-
617- const fetchKeychains = async ( wallet : IWallet ) : Promise < VerificationOptions [ 'keychains' ] > => {
618- return promiseProps ( {
619- user : this . keychains ( ) . get ( { id : wallet . keyIds ( ) [ KeyIndices . USER ] , reqId } ) ,
620- backup : this . keychains ( ) . get ( { id : wallet . keyIds ( ) [ KeyIndices . BACKUP ] , reqId } ) ,
621- bitgo : this . keychains ( ) . get ( { id : wallet . keyIds ( ) [ KeyIndices . BITGO ] , reqId } ) ,
622- } ) ;
623- } ;
624-
625- // obtain the keychains and key signatures
626- let keychains : VerificationOptions [ 'keychains' ] | undefined = verification . keychains ;
627- if ( ! keychains ) {
628- if ( disableNetworking ) {
629- throw new Error ( 'cannot fetch keychains without networking' ) ;
630- }
631- keychains = await fetchKeychains ( wallet ) ;
632- }
633-
634- if ( ! keychains || ! keychains . user || ! keychains . backup || ! keychains . bitgo ) {
635- throw new Error ( 'keychains are required, but could not be fetched' ) ;
636- }
637-
638- const keychainArray : Triple < Keychain > = [ keychains . user , keychains . backup , keychains . bitgo ] ;
639-
640- const keySignatures = _ . get ( wallet , '_wallet.keySignatures' , { } ) ;
641-
642- if ( _ . isUndefined ( txPrebuild . txHex ) ) {
643- throw new Error ( 'missing required txPrebuild property txHex' ) ;
644- }
645- // obtain all outputs
646- const explanation : TransactionExplanation = await this . explainTransaction < TNumber > ( {
647- txHex : txPrebuild . txHex ,
648- txInfo : txPrebuild . txInfo ,
649- pubs : keychainArray . map ( ( k ) => k . pub ) as Triple < string > ,
650- } ) ;
651- const allOutputs = [ ...explanation . outputs , ...explanation . changeOutputs ] ;
652-
653- let expectedOutputs ;
654- if ( txParams . rbfTxIds ) {
655- assert ( txParams . rbfTxIds . length === 1 ) ;
656-
657- const txToBeReplaced = await wallet . getTransaction ( { txHash : txParams . rbfTxIds [ 0 ] , includeRbf : true } ) ;
658- expectedOutputs = txToBeReplaced . outputs . flatMap (
659- ( output : { valueString : string ; address ?: string ; wallet ?: string } ) => {
660- // For self-sends, the walletId will be the same as the wallet's id
661- if ( output . wallet === wallet . id ( ) ) {
662- return [ ] ;
663- }
664- return [ this . toCanonicalTransactionRecipient ( output ) ] ;
665- }
666- ) ;
667- } else {
668- // verify that each recipient from txParams has their own output
669- expectedOutputs = _ . get ( txParams , 'recipients' , [ ] as TransactionRecipient [ ] ) . flatMap ( ( output ) => {
670- if ( output . address === undefined ) {
671- if ( output . amount . toString ( ) !== '0' ) {
672- throw new Error ( `Only zero amounts allowed for non-encodeable scriptPubkeys: ${ output } ` ) ;
673- }
674- return [ output ] ;
675- }
676- return [ { ...output , address : this . canonicalAddress ( output . address ) } ] ;
677- } ) ;
678- if ( params . txParams . allowExternalChangeAddress && params . txParams . changeAddress ) {
679- // when an external change address is explicitly specified, count all outputs going towards that
680- // address in the expected outputs (regardless of the output amount)
681- expectedOutputs . push (
682- ...allOutputs . flatMap ( ( output ) => {
683- if (
684- output . address === undefined ||
685- output . address !== this . canonicalAddress ( params . txParams . changeAddress as string )
686- ) {
687- return [ ] ;
688- }
689- return [ { ...output , address : this . canonicalAddress ( output . address ) } ] ;
690- } )
691- ) ;
692- }
693- }
694-
695- const missingOutputs = AbstractUtxoCoin . outputDifference ( expectedOutputs , allOutputs ) ;
696-
697- // get the keychains from the custom change wallet if needed
698- let customChange : CustomChangeOptions | undefined ;
699- const { customChangeWalletId = undefined } = wallet . coinSpecific ( ) || { } ;
700- if ( customChangeWalletId ) {
701- // fetch keychains from custom change wallet for deriving addresses.
702- // These keychains should be signed and this should be verified in verifyTransaction
703- const customChangeKeySignatures = wallet . _wallet . customChangeKeySignatures ;
704- const customChangeWallet : Wallet = await this . wallets ( ) . get ( { id : customChangeWalletId } ) ;
705- const customChangeKeys = await fetchKeychains ( customChangeWallet ) ;
706-
707- if ( ! customChangeKeys ) {
708- throw new Error ( 'failed to fetch keychains for custom change wallet' ) ;
709- }
710-
711- if ( customChangeKeys . user && customChangeKeys . backup && customChangeKeys . bitgo && customChangeWallet ) {
712- const customChangeKeychains : [ Keychain , Keychain , Keychain ] = [
713- customChangeKeys . user ,
714- customChangeKeys . backup ,
715- customChangeKeys . bitgo ,
716- ] ;
717-
718- customChange = {
719- keys : customChangeKeychains ,
720- signatures : [
721- customChangeKeySignatures . user ,
722- customChangeKeySignatures . backup ,
723- customChangeKeySignatures . bitgo ,
724- ] ,
725- } ;
726- }
727- }
728-
729- /**
730- * Loop through all the outputs and classify each of them as either internal spends
731- * or external spends by setting the "external" property to true or false on the output object.
732- */
733- const allOutputDetails : Output [ ] = await Promise . all (
734- allOutputs . map ( ( currentOutput ) => {
735- return parseOutput ( {
736- currentOutput,
737- coin : this ,
738- txPrebuild,
739- verification,
740- keychainArray,
741- wallet,
742- txParams,
743- customChange,
744- reqId,
745- } ) ;
746- } )
747- ) ;
748-
749- const needsCustomChangeKeySignatureVerification = allOutputDetails . some (
750- ( output ) => ( output as FixedScriptWalletOutput ) ?. needsCustomChangeKeySignatureVerification
751- ) ;
752-
753- const changeOutputs = _ . filter ( allOutputDetails , { external : false } ) ;
754-
755- // these are all the outputs that were not originally explicitly specified in recipients
756- // ideally change outputs or a paygo output that might have been added
757- const implicitOutputs = AbstractUtxoCoin . outputDifference ( allOutputDetails , expectedOutputs ) ;
758-
759- const explicitOutputs = AbstractUtxoCoin . outputDifference ( allOutputDetails , implicitOutputs ) ;
760-
761- // these are all the non-wallet outputs that had been originally explicitly specified in recipients
762- const explicitExternalOutputs = _ . filter ( explicitOutputs , { external : true } ) ;
763-
764- // this is the sum of all the originally explicitly specified non-wallet output values
765- const explicitExternalSpendAmount = utxolib . bitgo . toTNumber < TNumber > (
766- explicitExternalOutputs . reduce ( ( sum : bigint , o : Output ) => sum + BigInt ( o . amount ) , BigInt ( 0 ) ) as bigint ,
767- this . amountType
768- ) ;
769-
770- /**
771- * The calculation of the implicit external spend amount pertains to verifying the pay-as-you-go-fee BitGo
772- * automatically applies to transactions sending money out of the wallet. The logic is fairly straightforward
773- * in that we compare the external spend amount that was specified explicitly by the user to the portion
774- * that was specified implicitly. To protect customers from people tampering with the transaction outputs, we
775- * define a threshold for the maximum percentage of the implicit external spend in relation to the explicit
776- * external spend.
777- */
778-
779- // make sure that all the extra addresses are change addresses
780- // get all the additional external outputs the server added and calculate their values
781- const implicitExternalOutputs = _ . filter ( implicitOutputs , { external : true } ) ;
782- const implicitExternalSpendAmount = utxolib . bitgo . toTNumber < TNumber > (
783- implicitExternalOutputs . reduce ( ( sum : bigint , o : Output ) => sum + BigInt ( o . amount ) , BigInt ( 0 ) ) as bigint ,
784- this . amountType
785- ) ;
786-
787- return {
788- keychains,
789- keySignatures,
790- outputs : allOutputDetails ,
791- missingOutputs,
792- explicitExternalOutputs,
793- implicitExternalOutputs,
794- changeOutputs,
795- explicitExternalSpendAmount,
796- implicitExternalSpendAmount,
797- needsCustomChangeKeySignatureVerification,
798- customChange,
799- } ;
607+ return parseTransaction ( this , params ) ;
800608 }
801609
802610 /**
0 commit comments