Skip to content

Commit 2d9da33

Browse files
authored
Merge pull request #222 from BitGo/WCN-768/accelerate-integ-test
test: add integration test for tx acceleration
2 parents 64a241c + 10e0dcc commit 2d9da33

3 files changed

Lines changed: 168 additions & 4 deletions

File tree

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import 'should';
2+
import { startServices, IntegServices } from './helpers/setup';
3+
import { LOCALHOST } from './helpers/servers';
4+
import { SigningMode } from '../../shared/types';
5+
6+
/**
7+
* Deterministic test keypair derived from Buffer.alloc(64, 0x42) — a public, reproducible seed.
8+
* Not a secret. Never funded. Matches getKeychain.user.json and prebuildTx.tbtc.json.
9+
*/
10+
const USER_XPUB =
11+
'xpub661MyMwAqRbcEvJQx6spkkHLRgtjxmVdyDSvbDt2m9NFpbkHdcu5WJsHHHqFxNATbNHnhMWJiwckoMqF75EpcNhU9xeVM4oDS7urM3os4BH';
12+
const USER_XPRV =
13+
'xprv9s21ZrQH143K2SDwr5LpPcLbsf4FZJmnbzXKnqURCoqGwoR965apxWYoS2DKu2ivcMTB9uTK6XhZDEPfTeNXGf7mmACuMN6cFS5ttmrpZ3i';
14+
15+
const WALLET_ID = 'test-wallet-id';
16+
const CPFP_TX_ID = 'b8a828b98dbf32d9fd1875cbace9640ceb8c82626716b4a64203fdc79bb46d26';
17+
18+
const accelerateRequestBody = {
19+
pubkey: USER_XPUB,
20+
source: 'user' as const,
21+
cpfpTxIds: [CPFP_TX_ID],
22+
cpfpFeeRate: 50,
23+
maxFee: 10000,
24+
};
25+
26+
describe('Accelerate: EXTERNAL signing', () => {
27+
let services: IntegServices;
28+
29+
before(async () => {
30+
services = await startServices({ signingMode: SigningMode.EXTERNAL });
31+
});
32+
33+
after(async () => {
34+
await services.teardown();
35+
});
36+
37+
beforeEach(() => {
38+
services.keyProvider.calls.length = 0;
39+
services.bitgo.calls.length = 0;
40+
});
41+
42+
it('accelerates a tbtc transaction via CPFP using external key provider', async () => {
43+
const res = await fetch(
44+
`http://${LOCALHOST}:${services.mbePort}/api/v1/tbtc/advancedwallet/${WALLET_ID}/accelerate`,
45+
{
46+
method: 'POST',
47+
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer test-token' },
48+
body: JSON.stringify(accelerateRequestBody),
49+
},
50+
);
51+
52+
res.status.should.equal(200);
53+
const body = (await res.json()) as { txid: string; tx: string; status: string };
54+
body.should.have.property('txid', 'test-tx-id');
55+
body.should.have.property('tx', '01000000000101030a0000');
56+
body.should.have.property('status', 'signed');
57+
58+
/**
59+
* In external mode, AWM delegates signing to the key provider.
60+
* POST /sign must be called — not POST /key (no local key retrieval for signing).
61+
*/
62+
services.keyProvider.calls.filter((c) => c.path === '/sign').should.have.length(1);
63+
services.keyProvider.calls.filter((c) => c.path === '/key').should.have.length(0);
64+
65+
/** BitGo must receive tx/build with the correct cpfpTxIds, block/latest, and tx/send */
66+
const buildCalls = services.bitgo.calls.filter((c) => c.path.endsWith('/tx/build'));
67+
buildCalls.should.have.length(1);
68+
const buildBody = buildCalls[0].body as { cpfpTxIds?: string[] };
69+
buildBody.should.have.property('cpfpTxIds').which.deepEqual([CPFP_TX_ID]);
70+
71+
services.bitgo.calls
72+
.filter((c) => c.path.endsWith('/public/block/latest'))
73+
.should.have.length(1);
74+
services.bitgo.calls.filter((c) => c.path.endsWith('/tx/send')).should.have.length(1);
75+
});
76+
});
77+
78+
describe('Accelerate: LOCAL signing', () => {
79+
let services: IntegServices;
80+
81+
before(async () => {
82+
services = await startServices({ signingMode: SigningMode.LOCAL });
83+
84+
/**
85+
* Seed the mock key provider with a known xprv so AWM can retrieve it
86+
* via GET /key/:pub and sign the PSBT locally. The xpub must match
87+
* getKeychain.user.json and prebuildTx.accelerate.tbtc.json.
88+
*/
89+
await fetch(`http://127.0.0.1:${services.keyProvider.port}/key`, {
90+
method: 'POST',
91+
headers: { 'Content-Type': 'application/json' },
92+
body: JSON.stringify({
93+
pub: USER_XPUB,
94+
prv: USER_XPRV,
95+
coin: 'tbtc',
96+
source: 'user',
97+
type: 'independent',
98+
}),
99+
});
100+
});
101+
102+
after(async () => {
103+
await services.teardown();
104+
});
105+
106+
beforeEach(() => {
107+
services.keyProvider.calls.length = 0;
108+
services.bitgo.calls.length = 0;
109+
});
110+
111+
it('accelerates a tbtc transaction via CPFP using locally stored xprv', async () => {
112+
const res = await fetch(
113+
`http://${LOCALHOST}:${services.mbePort}/api/v1/tbtc/advancedwallet/${WALLET_ID}/accelerate`,
114+
{
115+
method: 'POST',
116+
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer test-token' },
117+
body: JSON.stringify(accelerateRequestBody),
118+
},
119+
);
120+
121+
res.status.should.equal(200);
122+
const body = (await res.json()) as { txid: string; tx: string; status: string };
123+
body.should.have.property('txid', 'test-tx-id');
124+
body.should.have.property('tx', '01000000000101030a0000');
125+
body.should.have.property('status', 'signed');
126+
127+
/**
128+
* In local mode, AWM retrieves the xprv via GET /key/:pub and signs internally.
129+
* POST /sign must NOT be called — signing happens inside AWM, not in the key provider.
130+
*/
131+
services.keyProvider.calls.filter((c) => c.path === '/sign').should.have.length(0);
132+
services.keyProvider.calls.filter((c) => c.path.startsWith('/key/')).length.should.be.above(0);
133+
134+
/** BitGo must receive tx/build with the correct cpfpTxIds, block/latest, and tx/send */
135+
const buildCalls = services.bitgo.calls.filter((c) => c.path.endsWith('/tx/build'));
136+
buildCalls.should.have.length(1);
137+
const buildBody = buildCalls[0].body as { cpfpTxIds?: string[] };
138+
buildBody.should.have.property('cpfpTxIds').which.deepEqual([CPFP_TX_ID]);
139+
140+
services.bitgo.calls
141+
.filter((c) => c.path.endsWith('/public/block/latest'))
142+
.should.have.length(1);
143+
services.bitgo.calls.filter((c) => c.path.endsWith('/tx/send')).should.have.length(1);
144+
});
145+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"txHex": "70736274ff01000a01000000000000000000000000",
3+
"txInfo": { "nP2SHInputs": 0, "nSegwitInputs": 0, "nOutputs": 0 }
4+
}

src/__tests__/integration/helpers/mockBitgoServer.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,22 @@ type SendManyFixtureMethod = 'getWallet' | 'prebuildTx' | 'sendTx';
2525
type SupportedCoin = 'hteth' | 'tbtc';
2626
type CoinToFixtures<C extends SupportedCoin> = {
2727
[K in SendManyFixtureMethod]: `${K}.${C}`;
28-
};
28+
} & { acceleratePrebuildTx: `prebuildTx.accelerate.${C}` | `prebuildTx.${C}` };
2929

3030
/** Registry — add a new coin here to support it across all sendMany integ test routes */
3131
const COIN_FIXTURES: { [C in SupportedCoin]: CoinToFixtures<C> } = {
32-
hteth: { getWallet: 'getWallet.hteth', prebuildTx: 'prebuildTx.hteth', sendTx: 'sendTx.hteth' },
33-
tbtc: { getWallet: 'getWallet.tbtc', prebuildTx: 'prebuildTx.tbtc', sendTx: 'sendTx.tbtc' },
32+
hteth: {
33+
getWallet: 'getWallet.hteth',
34+
prebuildTx: 'prebuildTx.hteth',
35+
sendTx: 'sendTx.hteth',
36+
acceleratePrebuildTx: 'prebuildTx.hteth', // CPFP/RBF not applicable to EVM; reuses standard prebuild
37+
},
38+
tbtc: {
39+
getWallet: 'getWallet.tbtc',
40+
prebuildTx: 'prebuildTx.tbtc',
41+
sendTx: 'sendTx.tbtc',
42+
acceleratePrebuildTx: 'prebuildTx.accelerate.tbtc',
43+
},
3444
};
3545

3646
function coinFixtures(coin: string): CoinToFixtures<SupportedCoin> {
@@ -99,7 +109,12 @@ export async function startMockBitgoServer(): Promise<MockBitgoServer> {
99109

100110
/** Transaction prebuild — coin-specific fixture */
101111
app.post('/api/v2/:coin/wallet/:walletId/tx/build', (req, res) => {
102-
res.json(loadFixture(coinFixtures(req.params.coin).prebuildTx));
112+
const { coin } = req.params;
113+
const isAccelerate = req.body?.cpfpTxIds?.length || req.body?.rbfTxIds?.length;
114+
const fixtureName = isAccelerate
115+
? coinFixtures(coin).acceleratePrebuildTx
116+
: coinFixtures(coin).prebuildTx;
117+
res.json(loadFixture(fixtureName));
103118
});
104119

105120
/** Transaction submit — coin-specific fixture */

0 commit comments

Comments
 (0)