Skip to content

Commit 6f14fe1

Browse files
committed
refactor: followups for splitting awm
Ticket: WCN-1082
1 parent 1d03528 commit 6f14fe1

11 files changed

Lines changed: 550 additions & 424 deletions

File tree

src/__tests__/api/advancedWalletManager/recoveryMultisigTransaction.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ describe('UTXO recovery — external signing mode', () => {
264264
backupNock.done();
265265
});
266266

267-
it('should return 500 if user sign call fails', async () => {
267+
it('surfaces key-provider signing failures as BitgoApiResponseError', async () => {
268268
nock(keyProviderUrl)
269269
.post('/sign', (body) => body.source === 'user')
270270
.reply(500, { message: 'HSM error' });
@@ -278,7 +278,8 @@ describe('UTXO recovery — external signing mode', () => {
278278
coin,
279279
});
280280

281-
response.status.should.equal(500);
281+
response.body.error.should.equal('BitGoApiResponseError');
282+
response.body.details.should.eql({ keySource: 'user' });
282283
});
283284

284285
it('keyToSign=user: calls user key provider only and returns a half-signed tx', async () => {

src/__tests__/api/master/asyncJobWorker.test.ts

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -676,36 +676,5 @@ describe('asyncJobWorker', () => {
676676
/expected txHex or halfSigned/,
677677
);
678678
});
679-
680-
it('uses awmBackupResponse as the final tx for split-AWM two-phase recovery', async () => {
681-
const halfSignedHex = 'half-signed-tx-hex';
682-
const fullSignedHex = 'full-signed-tx-hex';
683-
const job = makeRecoveryJob({
684-
awmResponse: awmOk({ txHex: halfSignedHex }),
685-
awmBackupResponse: awmOk({ txHex: fullSignedHex }),
686-
});
687-
688-
const updateNock = nock(BRIDGE_URL)
689-
.patch(
690-
`/job/${job.jobId}`,
691-
(body) => body.status === 'complete' && body.result?.txHex === fullSignedHex,
692-
)
693-
.reply(204);
694-
695-
await handleMultisigRecoveryOperation(job, bridge, bitgo);
696-
697-
updateNock.done();
698-
});
699-
700-
it('throws when awmBackupResponse is present but not a valid signed transaction', async () => {
701-
const job = makeRecoveryJob({
702-
awmResponse: awmOk({ txHex: 'half-signed-tx-hex' }),
703-
awmBackupResponse: { status: 200, body: { bad: 'shape' } },
704-
});
705-
706-
await handleMultisigRecoveryOperation(job, bridge, bitgo).should.be.rejectedWith(
707-
/expected txHex or halfSigned/,
708-
);
709-
});
710679
});
711680
});

src/__tests__/api/master/multisigRecoveryUtils.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ describe('multisigRecoveryUtils', () => {
8686
bridgeNock.done();
8787
});
8888

89-
it('submits with user,backup sources for split-AWM recovery', async () => {
89+
it('accepts multiple sources when explicitly passed', async () => {
9090
const jobId = 'job-456';
9191
const bridgeNock = nock(bridgeUrl)
9292
.post(`/api/${coin}/multisig/recovery`, (body) => {

src/__tests__/api/master/recoveryConsolidationsWallet.test.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import {
99
BitGoAPITestHarness,
1010
DEFAULT_ASYNC_MODE_CONFIG,
1111
makeMasterExpressTestConfig,
12+
makeSplitAwmMasterExpressConfig,
1213
nockAsyncMultisigRecoveryJob,
14+
nockAsyncRecoveryJobBypass,
15+
nockSplitAwmMultisigRecovery,
1316
} from './testUtils';
1417

1518
describe('POST /api/v1/:coin/advancedwallet/recoveryconsolidations', () => {
@@ -735,4 +738,74 @@ describe('POST /api/v1/:coin/advancedwallet/recoveryconsolidations', () => {
735738
);
736739
});
737740
});
741+
742+
describe('Split AWM (separate user and backup AWMs)', () => {
743+
const halfSignedTxHex = 'half-signed-trx-tx';
744+
const fullSignedTxHex = 'signed-trx-tx';
745+
746+
const trxConsolidationRequest = {
747+
multisigType: 'onchain' as const,
748+
userPub: mockUserPub,
749+
backupPub: mockBackupPub,
750+
bitgoPub: mockBitgoPub,
751+
tokenContractAddress: trxTokenContractAddress,
752+
startingScanIndex: 1,
753+
endingScanIndex: 3,
754+
};
755+
756+
function nockTrxTwoAddressConsolidationScan() {
757+
const tronBalanceWithToken = {
758+
data: [{ balance: 200_000_000, trc20: [{ [trxTokenContractAddress]: '1000000' }] }],
759+
};
760+
nock(tronBase).get(`/v1/accounts/${TRX_ADDR_1}`).reply(200, tronBalanceWithToken);
761+
nock(tronBase).post('/wallet/triggersmartcontract').reply(200, { transaction: TRON_MOCK_TX });
762+
nock(tronBase).get(`/v1/accounts/${TRX_ADDR_2}`).reply(200, tronBalanceWithToken);
763+
nock(tronBase).post('/wallet/triggersmartcontract').reply(200, { transaction: TRON_MOCK_TX });
764+
}
765+
766+
it('calls user AWM with keyToSign=user then backup AWM with keyToSign=backup per tx', async () => {
767+
nockTrxTwoAddressConsolidationScan();
768+
const { userAwmNock, backupAwmNock } = nockSplitAwmMultisigRecovery({
769+
coin: 'trx',
770+
halfSignedTxHex,
771+
fullSignedTxHex,
772+
times: 2,
773+
});
774+
775+
const response = await request
776+
.agent(expressApp(makeSplitAwmMasterExpressConfig()))
777+
.post('/api/v1/trx/advancedwallet/recoveryconsolidations')
778+
.set('Authorization', `Bearer ${accessToken}`)
779+
.send(trxConsolidationRequest);
780+
781+
response.status.should.equal(200);
782+
response.body.signedTxs.should.have.length(2);
783+
response.body.signedTxs[0].should.have.property('txHex', fullSignedTxHex);
784+
userAwmNock.done();
785+
backupAwmNock.done();
786+
});
787+
788+
it('stays sync when async mode is enabled (bridge is not called)', async () => {
789+
nockTrxTwoAddressConsolidationScan();
790+
const bridgeNock = nockAsyncRecoveryJobBypass('trx');
791+
const { userAwmNock, backupAwmNock } = nockSplitAwmMultisigRecovery({
792+
coin: 'trx',
793+
halfSignedTxHex,
794+
fullSignedTxHex,
795+
times: 2,
796+
});
797+
798+
const response = await request
799+
.agent(expressApp(makeSplitAwmMasterExpressConfig({ asyncEnabled: true })))
800+
.post('/api/v1/trx/advancedwallet/recoveryconsolidations')
801+
.set('Authorization', `Bearer ${accessToken}`)
802+
.send(trxConsolidationRequest);
803+
804+
response.status.should.equal(200);
805+
response.body.signedTxs.should.have.length(2);
806+
userAwmNock.done();
807+
backupAwmNock.done();
808+
bridgeNock.isDone().should.be.false();
809+
});
810+
});
738811
});

0 commit comments

Comments
 (0)