|
| 1 | +// TODO: type the handler with something like this |
1 | 2 | // export async function handleRecoveryWalletOnPrem( |
2 | 3 | // req: MasterApiSpecRouteRequest<'v1.wallet.recovery', 'post'>, |
3 | 4 | // ) { |
4 | | -// console.log(req); |
5 | 5 | // } |
6 | 6 |
|
7 | | -import { AbstractEthLikeNewCoins, RecoverOptions } from '@bitgo/abstract-eth'; |
| 7 | +import { SignFinalOptions } from '@bitgo/abstract-eth'; |
| 8 | +import { isEosCoin, isEthCoin, isStxCoin, isUtxoCoin, isXtzCoin } from '../shared/coinUtils'; |
8 | 9 | import { BitGoRequest } from '../types/request'; |
9 | 10 | import { createEnclavedExpressClient } from './enclavedExpressClient'; |
10 | 11 |
|
| 12 | +// TODO: this is gonna be present on eve so we can remove this |
| 13 | +const userEncryptedPrv = ''; |
| 14 | +const backupEncryptedPrv = ''; |
| 15 | +const passphrase = ''; |
| 16 | +// TODO: ---end remove vars |
| 17 | + |
11 | 18 | export async function handleRecoveryWalletOnPrem(req: BitGoRequest) { |
12 | 19 | const bitgo = req.bitgo; |
13 | 20 | const coin = req.params.coin; |
14 | | - // const { rootAddress, recoveryDestinationAddress, userPubKey, backupPubKey, coinSpecificParams } = |
15 | | - // req.body; |
16 | 21 |
|
17 | | - //TODO: delete this part |
18 | | - const userPubKey = ''; |
19 | | - const backupPubKey = ''; |
20 | | - const apiKey = ''; |
21 | | - const walletContractAddress = ''; |
22 | | - const recoveryDestinationAddress = ''; |
| 22 | + const { |
| 23 | + userPub, |
| 24 | + backupPub, |
| 25 | + walletContractAddress, |
| 26 | + recoveryDestinationAddress, |
| 27 | + coinSpecificParams, |
| 28 | + } = req.body; |
23 | 29 |
|
24 | 30 | const baseCoin = bitgo.coin(coin); |
25 | | - |
26 | 31 | const enclavedExpressClient = createEnclavedExpressClient(req.config, coin); |
27 | 32 | if (!enclavedExpressClient) { |
28 | 33 | throw new Error( |
29 | 34 | 'Enclaved express client not configured - enclaved express features will be disabled', |
30 | 35 | ); |
31 | 36 | } |
32 | 37 |
|
33 | | - // what's this? isEVM |
| 38 | + const sdkCoin = baseCoin; |
| 39 | + const commonRecoverParams = { |
| 40 | + userKey: userPub, |
| 41 | + backupKey: backupPub, |
| 42 | + walletContractAddress, |
| 43 | + recoveryDestination: recoveryDestinationAddress, |
| 44 | + // TODO: add api key here, currently configured on bitgo obj |
| 45 | + // apiKey, |
| 46 | + }; |
34 | 47 | if (baseCoin.isEVM()) { |
35 | | - let sdkCoin; |
36 | | - //TODO: do we need this cast to call recover? |
37 | | - if (true) { |
38 | | - sdkCoin = baseCoin as unknown as AbstractEthLikeNewCoins; |
39 | | - // } else if (isStxCoin(baseCoin)) { |
40 | | - // //TODO: what's the abstract coin class for stx, eos, btc, etc? |
41 | | - // sdkCoin = baseCoin as unknown as AbstractStxCoin; |
| 48 | + if (isEthCoin(sdkCoin)) { |
| 49 | + try { |
| 50 | + // TODO: populate coinSpecificParams with things like replayProtectionOptions |
| 51 | + // coinSpecificParams type could be "recoverOptions" |
| 52 | + const unsignedTx = await sdkCoin.recover({ |
| 53 | + ...commonRecoverParams, |
| 54 | + walletPassphrase: passphrase, |
| 55 | + }); |
| 56 | + |
| 57 | + const halfSignedTx = await sdkCoin.signTransaction({ |
| 58 | + txPrebuild: { ...unsignedTx } as unknown as SignFinalOptions, |
| 59 | + prv: bitgo.decrypt({ password: passphrase, input: userEncryptedPrv }), |
| 60 | + }); |
| 61 | + |
| 62 | + const { halfSigned } = halfSignedTx as any; |
| 63 | + const fullSignedTx = await sdkCoin.signTransaction({ |
| 64 | + isLastSignature: true, |
| 65 | + signingKeyNonce: halfSigned.signingKeyNonce ?? 0, |
| 66 | + backupKeyNonce: halfSigned.backupKeyNonce ?? 0, |
| 67 | + txPrebuild: { |
| 68 | + ...halfSigned, |
| 69 | + txHex: halfSigned.signatures, |
| 70 | + halfSigned, |
| 71 | + } as unknown as SignFinalOptions, |
| 72 | + prv: bitgo.decrypt({ password: passphrase, input: backupEncryptedPrv }), |
| 73 | + recipients: halfSigned.recipients ?? [], |
| 74 | + walletContractAddress: walletContractAddress, |
| 75 | + }); |
| 76 | + } catch (err) { |
| 77 | + console.log(err); |
| 78 | + throw err; |
| 79 | + } |
42 | 80 | } else { |
43 | 81 | throw new Error('Unsupported coin type for recovery: ' + coin); |
44 | 82 | } |
| 83 | + } else { |
| 84 | + // TODO (can't advance): XTZ throws a method not implemented on recover. |
| 85 | + if (isXtzCoin(sdkCoin)) { |
| 86 | + try { |
| 87 | + const unsignedTx = await sdkCoin.recover({ |
| 88 | + ...commonRecoverParams, |
| 89 | + }); |
| 90 | + |
| 91 | + //TODO: fill this fields, check output from recover when recover implemented on sdk for xtz |
| 92 | + const txHex = ''; |
| 93 | + const txInfo = 'txInfo' in unsignedTx ? unsignedTx.txInfo : undefined; |
| 94 | + const addressInfo = 'addressInfo' in unsignedTx ? unsignedTx.addressInfo : undefined; |
| 95 | + const feeInfo = 'feeInfo' in unsignedTx ? unsignedTx.feeInfo : undefined; |
| 96 | + const source = ''; |
| 97 | + const dataToSign = ''; |
45 | 98 |
|
46 | | - // Is the other class for xtz, eos, btc ==> AbstractUtxoCoin or do we have more specialization than that? |
| 99 | + const halfSignedTx = await sdkCoin.signTransaction({ |
| 100 | + txPrebuild: { |
| 101 | + txHex, |
| 102 | + txInfo, |
| 103 | + addressInfo, |
| 104 | + feeInfo, |
| 105 | + source, |
| 106 | + dataToSign, |
| 107 | + }, |
| 108 | + prv: bitgo.decrypt({ password: passphrase, input: userEncryptedPrv }), |
| 109 | + }); |
| 110 | + } catch (err) { |
| 111 | + console.log(err); |
| 112 | + throw err; |
| 113 | + } |
| 114 | + } else if (isStxCoin(sdkCoin)) { |
| 115 | + //TODO: (implementation untested): prioritize eth and btc instead of stc, when the other couple finished, go back to STX |
| 116 | + try { |
| 117 | + const unsignedTx = await sdkCoin.recover({ |
| 118 | + ...commonRecoverParams, |
| 119 | + rootAddress: walletContractAddress, // TODO: is a root address the same as wallet contract address? where does root address comes from if not? |
| 120 | + }); |
| 121 | + } catch (err) { |
| 122 | + console.log(err); |
| 123 | + throw err; |
| 124 | + } |
| 125 | + } else if (isEosCoin(sdkCoin)) { |
| 126 | + // TODO (implementation untested): we need some funds but faucets not working |
| 127 | + try { |
| 128 | + const unsignedTx = await sdkCoin.recover({ |
| 129 | + ...commonRecoverParams, |
| 130 | + }); |
| 131 | + } catch (err) { |
| 132 | + console.log(err); |
| 133 | + throw err; |
| 134 | + } |
| 135 | + } else if (isUtxoCoin(sdkCoin)) { |
| 136 | + //TODO (implementation untested): we need an API key to complete/test btc flow |
| 137 | + //TODO: do we need a special case for BTC or is another UTXO-based coin? |
47 | 138 |
|
48 | | - try { |
49 | | - // const { apiKey, walletContractAddress } = coinSpecificParams; |
| 139 | + const { bitgoPub } = coinSpecificParams || ''; |
| 140 | + try { |
| 141 | + const unsignedTx = await sdkCoin.recover({ |
| 142 | + ...commonRecoverParams, |
| 143 | + bitgoKey: bitgoPub, |
| 144 | + ignoreAddressTypes: coinSpecificParams?.ignoreAddressTypes || [], |
| 145 | + }); |
50 | 146 |
|
51 | | - // recover also ask for gasPrice, gasLimit, replayProtectionOptions, etc |
52 | | - // should we bring those from the coinSpecificParams or just let them empty? |
53 | | - const unsignedTx = await sdkCoin.recover({ |
54 | | - userKey: userPubKey, |
55 | | - backupKey: backupPubKey, |
56 | | - walletContractAddress, |
57 | | - recoveryDestination: recoveryDestinationAddress, |
58 | | - apiKey, |
59 | | - walletPassphrase: '^.u0UWaTI;cIx!xi9Ya1', |
60 | | - } as any as RecoverOptions); |
61 | | - console.log('unsigned tx payload'); |
62 | | - console.log(JSON.stringify(unsignedTx)); |
63 | | - } catch (err) { |
64 | | - console.log(err); |
| 147 | + // some guards as the types have some imcompatibilities issues |
| 148 | + const txInfo = 'txInfo' in unsignedTx ? unsignedTx.txInfo : undefined; |
| 149 | + const txHex = 'txHex' in unsignedTx ? unsignedTx.txHex : ''; |
| 150 | + |
| 151 | + const halfSignedTx = await sdkCoin.signTransaction({ |
| 152 | + txPrebuild: { |
| 153 | + txHex, |
| 154 | + txInfo, |
| 155 | + }, |
| 156 | + prv: bitgo.decrypt({ password: passphrase, input: userEncryptedPrv }), |
| 157 | + }); |
| 158 | + |
| 159 | + const fullSignedTx = await sdkCoin.signTransaction({ |
| 160 | + //TODO: check the body of this based on halfSignedTx output |
| 161 | + isLastSignature: true, |
| 162 | + txPrebuild: { |
| 163 | + txHex, |
| 164 | + txInfo, |
| 165 | + }, |
| 166 | + signingStep: 'cosignerNonce', |
| 167 | + }); |
| 168 | + } catch (err) { |
| 169 | + console.log(err); |
| 170 | + throw err; |
| 171 | + } |
| 172 | + } else { |
| 173 | + throw new Error('Unsupported coin type for recovery: ' + coin); |
65 | 174 | } |
66 | 175 | } |
67 | 176 | } |
0 commit comments