11import { SignFinalOptions } from '@bitgo/abstract-eth' ;
2- import { MethodNotImplementedError } from 'bitgo' ;
32import { EnclavedApiSpecRouteRequest } from '../../enclavedBitgoExpress/routers/enclavedApiSpec' ;
4- import { KmsClient } from '../../kms/kmsClient' ;
53import logger from '../../logger' ;
6- import { isEosCoin , isEthCoin , isStxCoin , isUtxoCoin , isXtzCoin } from '../../shared/coinUtils' ;
4+ import { isEthCoin } from '../../shared/coinUtils' ;
5+ import { retrieveKmsKey } from './utils' ;
76
87export async function recoveryMultisigTransaction (
98 req : EnclavedApiSpecRouteRequest < 'v1.multisig.recovery' , 'post' > ,
109) : Promise < any > {
11- const {
12- userPub,
13- backupPub,
14- walletContractAddress,
15- recoveryDestinationAddress,
16- recoveryParams,
17- apiKey,
18- } = req . body ;
10+ const { userPub, backupPub, unsignedSweepPrebuildTx, walletContractAddress } = req . body ;
1911
2012 //fetch prv and check that pub are valid
2113 const userPrv = await retrieveKmsKey ( { pub : userPub , source : 'user' } ) ;
22- const backupPrv = await retrieveKmsKey ( { pub : backupPub , source : 'user ' } ) ;
14+ const backupPrv = await retrieveKmsKey ( { pub : backupPub , source : 'backup ' } ) ;
2315
2416 if ( ! userPrv || ! backupPrv ) {
2517 const errorMsg = `Error while recovery wallet, missing prv keys for user or backup on pub keys user=${ userPub } , backup=${ backupPub } ` ;
@@ -30,16 +22,6 @@ export async function recoveryMultisigTransaction(
3022 const bitgo = req . bitgo ;
3123 const coin = bitgo . coin ( req . params . coin ) ;
3224
33- //construct a common payload for the recovery that it's repeated in any kind of recovery
34- const commonRecoveryParams = {
35- userKey : userPub ,
36- backupKey : backupPub ,
37- walletContractAddress,
38- recoveryDestination : recoveryDestinationAddress ,
39- // TODO: api key is not used so far because of a missconfig error on the bitgo obj
40- apiKey,
41- } ;
42-
4325 // The signed transaction format depends on the coin type so we do this check as a guard
4426 // If you check the type of coin before and after the "if", you may see "BaseCoin" vs "AbstractEthLikeCoin"
4527 if ( coin . isEVM ( ) ) {
@@ -48,16 +30,11 @@ export async function recoveryMultisigTransaction(
4830 // TODO: populate coinSpecificParams with things like replayProtectionOptions
4931 // coinSpecificParams type could be "recoverOptions"
5032 try {
51- const unsignedTx = await coin . recover ( {
52- ...commonRecoveryParams ,
53- //TODO: it's needed for keycard debugging, the walletPassphrase
54- //walletPassphrase: passphrase,
55- } ) ;
56-
5733 const halfSignedTx = await coin . signTransaction ( {
5834 isLastSignature : false ,
5935 prv : userPrv ,
60- txPrebuild : { ...unsignedTx } as unknown as SignFinalOptions ,
36+ txPrebuild : { ...unsignedSweepPrebuildTx } as unknown as SignFinalOptions ,
37+ walletContractAddress,
6138 } ) ;
6239
6340 const { halfSigned } = halfSignedTx as any ;
@@ -68,7 +45,9 @@ export async function recoveryMultisigTransaction(
6845 ...halfSignedTx ,
6946 txHex : halfSigned . signatures ,
7047 halfSigned,
71- } ,
48+ recipients : halfSigned . recipients ?? [ ] ,
49+ } as unknown as SignFinalOptions ,
50+ walletContractAddress,
7251 signingKeyNonce : halfSigned . signingKeyNonce ?? 0 ,
7352 backupKeyNonce : halfSigned . backupKeyNonce ?? 0 ,
7453 recipients : halfSigned . recipients ?? [ ] ,
@@ -85,135 +64,6 @@ export async function recoveryMultisigTransaction(
8564 throw new Error ( errorMsg ) ;
8665 }
8766 } else {
88- // TODO: from now on, this part isn't tested as we're lacking funds/apiKeys/etc
89- // TODO: WIP
90- // TODO (can't advance): XTZ throws a method not implemented on recover.
91- if ( isXtzCoin ( coin ) ) {
92- try {
93- const unsignedTx = await coin . recover ( {
94- ...commonRecoveryParams ,
95- } ) ;
96-
97- //TODO: fill this fields, check output from recover when recover implemented on sdk for xtz
98- const txHex = '' ;
99- const txInfo = 'txInfo' in unsignedTx ? unsignedTx . txInfo : undefined ;
100- const addressInfo = 'addressInfo' in unsignedTx ? unsignedTx . addressInfo : undefined ;
101- const feeInfo = 'feeInfo' in unsignedTx ? unsignedTx . feeInfo : undefined ;
102- const source = '' ;
103- const dataToSign = '' ;
104-
105- const halfSignedTx = await coin . signTransaction ( {
106- txPrebuild : {
107- txHex,
108- txInfo,
109- addressInfo,
110- feeInfo,
111- source,
112- dataToSign,
113- } ,
114- prv : userPrv ,
115- } ) ;
116- //TODO: continue with full sign and return that
117- // still needs to be tested in order to deduce min payload
118- return halfSignedTx ;
119- } catch ( err ) {
120- console . log ( err ) ;
121- throw err ;
122- }
123- } else if ( isStxCoin ( coin ) ) {
124- //TODO: (implementation untested): prioritize eth and btc instead of stc, when the other couple finished, go back to STX
125- try {
126- const unsignedTx = await coin . recover ( {
127- ...commonRecoveryParams ,
128- rootAddress : walletContractAddress , // TODO: is a root address the same as wallet contract address? where does root address comes from if not?
129- } ) ;
130- //TODO: continue with half sign and return that
131- return unsignedTx ;
132- } catch ( err ) {
133- console . log ( err ) ;
134- throw err ;
135- }
136- } else if ( isEosCoin ( coin ) ) {
137- // TODO (implementation untested): we need some funds but faucets not working
138- try {
139- const unsignedTx = await coin . recover ( {
140- ...commonRecoveryParams ,
141- } ) ;
142-
143- //TODO: continue with half sign and return that
144- return unsignedTx ;
145- } catch ( err ) {
146- console . log ( err ) ;
147- throw err ;
148- }
149- } else if ( isUtxoCoin ( coin ) ) {
150- //TODO (implementation untested): we need an API key to complete/test btc flow
151- //TODO: do we need a special case for BTC or is another UTXO-based coin?
152-
153- const { bitgoPub } = recoveryParams ;
154- if ( ! bitgoPub ) {
155- logger . error ( 'Missing bitgoPub in recoveryParams for UTXO coin recovery' ) ;
156- throw new Error ( 'Missing bitgoPub in recoveryParams for UTXO coin recovery' ) ;
157- }
158- try {
159- const unsignedTx = await coin . recover ( {
160- ...commonRecoveryParams ,
161- bitgoKey : bitgoPub ,
162- ignoreAddressTypes : recoveryParams . ignoreAddressTypes || [ ] ,
163- } ) ;
164-
165- // some guards as the types have some imcompatibilities issues
166- const txInfo = 'txInfo' in unsignedTx ? unsignedTx . txInfo : undefined ;
167- const txHex = 'txHex' in unsignedTx ? unsignedTx . txHex : '' ;
168-
169- const halfSignedTx = await coin . signTransaction ( {
170- txPrebuild : {
171- txHex,
172- txInfo,
173- } ,
174- prv : userPrv ,
175- } ) ;
176-
177- const fullSignedTx = await coin . signTransaction ( {
178- //TODO: check the body of this based on halfSignedTx output
179- isLastSignature : true ,
180- txPrebuild : {
181- txHex,
182- txInfo,
183- } ,
184- signingStep : 'cosignerNonce' ,
185- } ) ;
186-
187- console . log ( halfSignedTx ) ;
188- throw new MethodNotImplementedError (
189- 'Full signing for UTXO coins is not implemented in recovery yet. Please implement it.' ,
190- ) ;
191-
192- return fullSignedTx ;
193- } catch ( err ) {
194- console . log ( err ) ;
195- throw err ;
196- }
197- } else {
198- throw new Error ( 'Unsupported coin type for recovery: ' + coin ) ;
199- }
200- }
201- }
202-
203- // TODO: this function is duplicated in multisigTransactioSign.ts but as hardcoded.
204- // move both to an utils file
205- async function retrieveKmsKey ( { pub, source } : { pub : string ; source : string } ) : Promise < string > {
206- const kms = new KmsClient ( ) ;
207- // Retrieve the private key from KMS
208- let prv : string ;
209- try {
210- const res = await kms . getKey ( { pub, source } ) ;
211- prv = res . prv ;
212- return prv ;
213- } catch ( error : any ) {
214- throw {
215- status : error . status || 500 ,
216- message : error . message || 'Failed to retrieve key from KMS' ,
217- } ;
67+ throw new Error ( 'Unsupported coin type for recovery: ' + coin ) ;
21868 }
21969}
0 commit comments