@@ -1932,6 +1932,36 @@ export class BitGoAPI implements BitGoBase {
19321932 password : this . calculateHMAC ( user . username , newPassword ) ,
19331933 } ;
19341934
1935+ // Calculate payload size in KB
1936+ const payloadSizeBytes = JSON . stringify ( updatePasswordParams ) . length ;
1937+ const payloadSizeKB = Math . ceil ( payloadSizeBytes / 1024 ) ;
1938+
1939+ // Check if batching flow is enabled
1940+ try {
1941+ const batchingFlowCheck = await this . get ( this . url ( '/user/checkBatchingPasswordFlow' , 2 ) )
1942+ . query ( { payloadSize : payloadSizeKB . toString ( ) } )
1943+ . result ( ) ;
1944+
1945+ if ( batchingFlowCheck . isBatchingFlowEnabled ) {
1946+ await this . processKeychainPasswordUpdatesInBatches (
1947+ updatePasswordParams . keychains ,
1948+ updatePasswordParams . v2_keychains ,
1949+ batchingFlowCheck . noOfBatches ,
1950+ 3
1951+ ) ;
1952+ // Call changepassword API without keychains for batching flow
1953+ return this . post ( this . url ( '/user/changepassword' ) )
1954+ . send ( {
1955+ version : updatePasswordParams . version ,
1956+ oldPassword : updatePasswordParams . oldPassword ,
1957+ password : updatePasswordParams . password ,
1958+ } )
1959+ . result ( ) ;
1960+ }
1961+ } catch ( error ) {
1962+ // batching flow check failed
1963+ }
1964+
19351965 return this . post ( this . url ( '/user/changepassword' ) ) . send ( updatePasswordParams ) . result ( ) ;
19361966 }
19371967
@@ -2173,4 +2203,63 @@ export class BitGoAPI implements BitGoBase {
21732203 const result = await req ;
21742204 return result . body ;
21752205 }
2206+
2207+ /**
2208+ * Process keychain password updates in batches with retry logic
2209+ * @param keychains - The v1 keychains to update
2210+ * @param v2Keychains - The v2 keychains to update
2211+ * @param noOfBatches - Number of batches to split the keychains into
2212+ * @param maxRetries - Maximum number of retries per batch
2213+ * @private
2214+ */
2215+ private async processKeychainPasswordUpdatesInBatches (
2216+ keychains : Record < string , string > ,
2217+ v2Keychains : Record < string , string > ,
2218+ noOfBatches : number ,
2219+ maxRetries : number
2220+ ) : Promise < void > {
2221+ // Split keychains into batches
2222+ const v1KeychainEntries = Object . entries ( keychains ) ;
2223+ const v2KeychainEntries = Object . entries ( v2Keychains ) ;
2224+
2225+ const v1BatchSize = Math . ceil ( v1KeychainEntries . length / noOfBatches ) ;
2226+ const v2BatchSize = Math . ceil ( v2KeychainEntries . length / noOfBatches ) ;
2227+
2228+ // Call batching API for each batch with retry logic
2229+ for ( let i = 0 ; i < noOfBatches ; i ++ ) {
2230+ const v1Batch = Object . fromEntries ( v1KeychainEntries . slice ( i * v1BatchSize , ( i + 1 ) * v1BatchSize ) ) ;
2231+ const v2Batch = Object . fromEntries ( v2KeychainEntries . slice ( i * v2BatchSize , ( i + 1 ) * v2BatchSize ) ) ;
2232+
2233+ let retryCount = 0 ;
2234+ let success = false ;
2235+
2236+ while ( retryCount < maxRetries && ! success ) {
2237+ try {
2238+ const response = await this . put ( this . url ( '/user/keychains' , 2 ) )
2239+ . send ( {
2240+ keychains : v1Batch ,
2241+ v2_keychains : v2Batch ,
2242+ } )
2243+ . result ( ) ;
2244+
2245+ // Check if there are any failed keychains in the response
2246+ const hasFailed =
2247+ ( response . failed ?. v1 && Object . keys ( response . failed . v1 ) . length > 0 ) ||
2248+ ( response . failed ?. v2 && Object . keys ( response . failed . v2 ) . length > 0 ) ;
2249+
2250+ if ( hasFailed ) {
2251+ throw new Error ( `Batch ${ i + 1 } had failed keychains: ${ JSON . stringify ( response . failed ) } ` ) ;
2252+ }
2253+
2254+ success = true ;
2255+ } catch ( error ) {
2256+ retryCount ++ ;
2257+
2258+ if ( retryCount >= maxRetries ) {
2259+ throw new Error ( `Batch ${ i + 1 } failed after ${ maxRetries } retries: ${ error . message } ` ) ;
2260+ }
2261+ }
2262+ }
2263+ }
2264+ }
21762265}
0 commit comments