Skip to content

Commit ab167b4

Browse files
Merge pull request #51 from BitGo/BTC-2652.bitgo-psbt-parse-other-wallet-output
feat(wasm-utxo): improve transaction parsing with external outputs support
2 parents ef8124f + a05e620 commit ab167b4

File tree

60 files changed

+3032
-902
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+3032
-902
lines changed

packages/wasm-utxo/cli/test/fixtures/psbt_bitcoin_fullsigned.txt

Lines changed: 62 additions & 34 deletions
Large diffs are not rendered by default.

packages/wasm-utxo/cli/test/fixtures/psbt_raw_bitcoin_fullsigned.txt

Lines changed: 83 additions & 54 deletions
Large diffs are not rendered by default.
Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,68 @@
11
tx: None
22
├─ version: 1i32
33
├─ lock_time: 0u32
4-
├─ txid: a341d563b6d62d3c078017ae60a72fae551f7381634871b2764422e9d4968061 (32 bytes)
5-
├─ ntxid: 819e1ce50e5f92d52f3d6cfbb4084355392777d8b23b0bce674674577a940608 (32 bytes)
6-
├─ wtxid: bfb14a40525fa2a173ce7fc0fb4f7fd7d89b07f5b0ea0073e77487de3c0b2d97 (32 bytes)
4+
├─ txid: 895306f11e2bdb8d0ba6eca19c862ed0cab3391a3e02cffd8a02d386eea67e8b (32 bytes)
5+
├─ ntxid: a8f0a1efb5a77f4fe2e40c379d3e122d3b30ac0b88aa80c6e70d3fd72a6cf736 (32 bytes)
6+
├─ wtxid: d93620afa90de598a520acf58551308a9d141f16c276261dcbff2f57828d2c87 (32 bytes)
77
├─ inputs: 7u64
88
│ ├─ input_0: None
9-
│ │ ├─ prev_txid: d99efae199fc4d54e5825a8e3368a395e9eb329915cfb5d88dc311507d184836 (32 bytes)
9+
│ │ ├─ prev_txid: 7cce12effc2ea564c4227333fd305447e0de7b3ae7a5fcc751effff3ea1377e1 (32 bytes)
1010
│ │ ├─ prev_vout: 0u32
1111
│ │ ├─ sequence: 4294967295u32
12-
│ │ └─ script_sig: 0047304402201e79545c011e34b1b872b4b057e66262d86881da6a7f853e7e57a9a4d12c6dc102201a538b2175c7b7161c76da18394220f354ae2729fcc1c902b74c59cd79e0153101483045022100dd31a25d0ebea90e67910168ef7c2b383ca26192cd5c8709f02980b7ca90472802203795c65ee0f073b80644d0c8ffa1d428457ae2af3df5b5d8117949781d10c462014c69522103f6f40764bd5d63f200a2778883acf75e96f15095c998263c087270d0c97e7e7f21035ffb7abc70159e0469f4b989a6d5e1785a2904169ff050b2f468fe5d3d5dbbf22103e1524d7f6fc57ab3eacbb659b787106780a475d1db483952c2310b7e9a38975b53ae (253 bytes)
12+
│ │ └─ script_sig: 00473044022063f7d77b3e78d916fef10e0e3249730b0b364a07bd4d1de482e7dc3a6910e13402204925ac2db7e1e6a51970f288a12d88d8abfec4fc3b44686664f5a6d707e6b27101473044022068576594f32f0196dce83d0a2fadde3e3670ae4cee22295337c406374a14496602205858f9cbf54285930c2004045440956db1953a67d305ca3db0206ffd7e7d5343014c69522103f6f40764bd5d63f200a2778883acf75e96f15095c998263c087270d0c97e7e7f21035ffb7abc70159e0469f4b989a6d5e1785a2904169ff050b2f468fe5d3d5dbbf22103e1524d7f6fc57ab3eacbb659b787106780a475d1db483952c2310b7e9a38975b53ae (252 bytes)
1313
│ ├─ input_1: None
14-
│ │ ├─ prev_txid: 1ec9729a1b25373a30f0fe6e0879b31136a54f2f8c98618c46862748a08c5e78 (32 bytes)
14+
│ │ ├─ prev_txid: b9c7b288fd6aa0d1415996e2a5dad9a4e070c129e179277f53e32f602f180646 (32 bytes)
1515
│ │ ├─ prev_vout: 1u32
1616
│ │ ├─ sequence: 4294967295u32
1717
│ │ ├─ script_sig: 22002046d4800a1393330196085399ed4a1700b4c2fd38c52188c3a3a91f721f3c9600 (35 bytes)
1818
│ │ └─ witness: 4u64
1919
│ │ ├─ item_0: (0 bytes)
20-
│ │ ├─ item_1: 304402205f760c27e7598a9e80a2adaf1d925c67d2e6c24aec773e5cce56d234159ab1e502201ce97d5659681ab881e10acc9c8021587f843a15336c3a8b6fb2efc053928b8501 (71 bytes)
21-
│ │ ├─ item_2: 3045022100df41fbcbf3c7f77182627bcb7cd17973b8ca58c16b81de98e94c0293dcf33ac202206017171914fdfbc80f933ab3b5aae4e3d36e968757e6c0788ffbeaaebc68e21e01 (72 bytes)
20+
│ │ ├─ item_1: 3045022100c3dddbc8b20f4aac003165182fb72357486da8a1538d151b06aca94f7894e169022050756f3949e780fd15e3a25baf7b3b3f674422c7ee8aff1d2f77a0f4d7f42ada01 (72 bytes)
21+
│ │ ├─ item_2: 304402204f46d55ab3c78be2dd7b2df1a04aa929478c32445ea948923dc36f13eea30d23022054db1d0f3e7922e1ee1e7c3610b73c0297050c37cc4ac12e8ac63cb71dc314a501 (71 bytes)
2222
│ │ └─ item_3: 522102a585f3fab49b5ef95346d932221a221bd55ec191f15533e0f270b3582574f0352102c4f7866cfd5996bc1068a96313ca15e9ccb5e984583a07340dddae7af014605b2102234531bc9119a36a7946e95aeb74915dd087c7b44f517b8eeeb73f89a0ff1a9953ae (105 bytes)
2323
│ ├─ input_2: None
24-
│ │ ├─ prev_txid: bb0dfbeba59bba69e3ce07a54346f804c543fa46d828ed539d11231adbaa8b7b (32 bytes)
24+
│ │ ├─ prev_txid: 1f57d9560ae9071600b696533192dc17799164fd3fa12804a7f7eb4201c4d5dd (32 bytes)
2525
│ │ ├─ prev_vout: 2u32
2626
│ │ ├─ sequence: 4294967295u32
2727
│ │ ├─ script_sig: (0 bytes)
2828
│ │ └─ witness: 4u64
2929
│ │ ├─ item_0: (0 bytes)
30-
│ │ ├─ item_1: 3045022100d3b03fb77553144e14171c7b1b517a3bf33df87370b65e337f0a50844562261e0220585bc0913b56ad968161d343f321d77f5f94ff3cf62545932f1509239a15a38e01 (72 bytes)
31-
│ │ ├─ item_2: 304402206441089c8c71a4926b49a5ab12bfaa86c4b64abbc519b3d217d68fe71bc43bee02203e17d0752c0b411e9477c22c2f4556a62da39e60095c6e0774bf42aa8f491b4d01 (71 bytes)
30+
│ │ ├─ item_1: 304402207017fae20521ac634cfaf259714d370efcd196f83fb8d7c3133207900f27355902201e9b7f217987da96f77b3ba33e868987d13257cc03a05af959e34be2719fe1b501 (71 bytes)
31+
│ │ ├─ item_2: 3045022100a9a571f171172962c80bf97099c0f1a809bec183af83dabe65bfc51ff1084548022047376a83e5d8bd5bfb07c5720bf90535d9a0ba5837172235132530387618b95901 (72 bytes)
3232
│ │ └─ item_3: 522103519e572ab468560d1f8d0f87699d098308e166905f5dcae4390f060faa8f1ce32103a05bc26391221d685e18daaff36a1e95c34f99451253c16cd42650db278a79112102e80a08885c353676b820cdc290e6d81a0ac95a03e8abbf4bb7aeebfb70feb41853ae (105 bytes)
3333
│ ├─ input_3: None
34-
│ │ ├─ prev_txid: a662a5ffebf58bf3e65e53456c67c029cc154a2409740d8cd37fdee7559c5efd (32 bytes)
34+
│ │ ├─ prev_txid: fc8fe5ff2a818c7661a4629809df2a1ffd60b6d16549fbf4882b54e7413ff8e6 (32 bytes)
3535
│ │ ├─ prev_vout: 3u32
3636
│ │ ├─ sequence: 4294967295u32
3737
│ │ ├─ script_sig: (0 bytes)
3838
│ │ └─ witness: 4u64
39-
│ │ ├─ item_0: e7dab0d277ce7c41933136b44dcabef400f1d0e622a2255f9867d807bc8e0008cfd30766d36d736c8df7aa32917de59d0832b481c499f0917876134532306d45 (64 bytes)
40-
│ │ ├─ item_1: 83b1bb7c6ce5868c8a723c4d350591894809b1335586f60cbfccc6dd4e28b8630a74637b1b242e5a389540db94f23bcbedee9c783830cb2afaff1f2763dbdb7b (64 bytes)
39+
│ │ ├─ item_0: ce28402fd5d3544b9f3a8f21bc122c2147a9b17256458eb5b69b33c0ea5d2531b72108238a7493f2dd264221a87119b5343c16dcc48e941ba507243624f3d3c8 (64 bytes)
40+
│ │ ├─ item_1: be30d2f4e624a99d258f9de83971d249611d1956908b64c8914fe2a347ecd9547b7b67aa6f544e9b5b21d265f7f2b83c67c40c1fc6e22deabbdc320ed1fc6080 (64 bytes)
4141
│ │ ├─ item_2: 207373d723ce0a87f8fdd66843be12dae9d51939f68188d5ad96920bef5f51f496ad20aa88ca194fda43486504b86ac9b71f10fc41de7b1713874839427f74e78c2d5aac (68 bytes)
4242
│ │ └─ item_3: c0bc17ea2bbf6ff46bd5c0c3780be2a63880eb7bb782b39ed86f29b99f4882994e1b178b1b55d3c6aac7b039e3422dfdb9c9d75d4d6fc908e9743e88b651e48d83 (65 bytes)
4343
│ ├─ input_4: None
44-
│ │ ├─ prev_txid: d394375cee23b3b9951488c0403529157a85b058edc0351546d9eb4807e8a1d3 (32 bytes)
44+
│ │ ├─ prev_txid: 998804e6718d94c77eef1e8b3cd787bcc2dbfcbd6c009ab33043e7e500d5b49b (32 bytes)
4545
│ │ ├─ prev_vout: 4u32
4646
│ │ ├─ sequence: 4294967295u32
4747
│ │ ├─ script_sig: (0 bytes)
4848
│ │ └─ witness: 4u64
49-
│ │ ├─ item_0: 4d8e50128b5c61dd449b352a6d6f3d611e163b0794c8c2d057d25401957bb07a70420e336e18f6b255d5b869570c087091c30c1a33f0cac0a40d90d0514c69a4 (64 bytes)
50-
│ │ ├─ item_1: 0a1e013f6d57fb8b248b2b19b08b34c1a3cd5ff6de4acdf19dd4ce0e02d42154856dd569f4668f2abaccbb50ce81a9d2f53efdfb627c2cb14cf92c4189a35da7 (64 bytes)
49+
│ │ ├─ item_0: 63fb7b832f6aa0f406554c195a96086b7e14468e4622b8e9976b3a83dca53a7c9152649a6d42d77c91d078eee2044e40c9ded9c90c0ee73f73930e38480f5063 (64 bytes)
50+
│ │ ├─ item_1: 48ee7fb32dc07262ed7be7d907734df486ae6e54018d0b109518c7ae344366f72a234bdf28bce46a9fb1ef49e9292c26a4d1ce228936bf2824c38bf866ba371a (64 bytes)
5151
│ │ ├─ item_2: 20643151ab03f3a97bb86d3592f12f8d25a8026d89cd47342fc1b49c5e2e63478bad20035c9d632db2ddb8f90cf3ccfc931822bcb1f24562b23ae041abf12e54bca64dac (68 bytes)
5252
│ │ └─ item_3: c1a107a403f7a207e7b1f552d3fd3f55f64ae40e149d0d26f4868ad7faec949eb7637e37cac6e5f22347ab68e876b559117d9597ee7b37fb983c2ab9e51023c976 (65 bytes)
5353
│ ├─ input_5: None
54-
│ │ ├─ prev_txid: 0d2f7f3486edaefc6ab92c21f7caf90085c4de4adac491106fd8a0e4f563dea3 (32 bytes)
54+
│ │ ├─ prev_txid: 02b23e9ffd550fa053f4c43e097b8114e463c8ba52c72b30d31d10cde853aabf (32 bytes)
5555
│ │ ├─ prev_vout: 5u32
5656
│ │ ├─ sequence: 4294967295u32
5757
│ │ ├─ script_sig: (0 bytes)
5858
│ │ └─ witness: 1u64
59-
│ │ └─ item_0: 2ea4aeeef7c10765ed7bdce7ac429b7395e08cf55083df38384781054b0880e244fe8a2d847a8c334431322d0eeba4e34872ef6052e2044fff7fa8763ce0c20b (64 bytes)
59+
│ │ └─ item_0: 0cfa38061dca7d6281adb9d2211757e54519b82cb27071e516ef86a7b8f8825988376fea2664afbdb2de6f9923f53dbbb7f5afd1f919f96c756e31da4342dec4 (64 bytes)
6060
│ └─ input_6: None
61-
│ ├─ prev_txid: 97441d99a8d66f124ab3c9de26b87bd00aeb1547051c842a88165c1b089ee902 (32 bytes)
61+
│ ├─ prev_txid: 200d021345d40b204cc22a07ebad78da17159f34c2c656a2f4a51d68c6ca7754 (32 bytes)
6262
│ ├─ prev_vout: 6u32
6363
│ ├─ sequence: 4294967295u32
64-
│ └─ script_sig: 483045022100d2a7abb6c0563c3000e37412a9b97f6352d17e40960456fe830d264f0474f8b6022038d5cdbe88a319de5d7b7db8f57d0a92524beb242729b46a617adb530f2fa0d00123210336ef228ffe9b8efffba052c32d334660dd1f8366cf8fe44ae5aa672b6b629095ac (109 bytes)
65-
└─ outputs: 5u64
64+
│ └─ script_sig: 473044022066ba18beb6d351e087fe1203f4a0e47ddcc035882f9cd30a83e2d92febe9eff90220664396d8ebdcd04bef62a0781c1261279493b2ad88dc79eddc55f11e3ebcdb9d0123210336ef228ffe9b8efffba052c32d334660dd1f8366cf8fe44ae5aa672b6b629095ac (108 bytes)
65+
└─ outputs: 8u64
6666
├─ output_0: None
6767
│ ├─ value: 900u64
6868
│ ├─ script_pubkey: a914d909474404c124a3d04c3fbff61faa49cf43c58b87 (23 bytes)
@@ -79,7 +79,18 @@ tx: None
7979
│ ├─ value: 900u64
8080
│ ├─ script_pubkey: 5120b27227f5cadc056afea4b02b4b97b9a0151786234c26ad588dccf134e78931c6 (34 bytes)
8181
│ └─ address: bc1pkfez0aw2mszk4l4ykq45h9ae5q230p3rfsn26kydencnfeufx8rq23ty2z
82-
└─ output_4: None
82+
├─ output_4: None
83+
│ ├─ value: 900u64
84+
│ ├─ script_pubkey: 51204f73b5561399eb397b5ab413e090cc0c48ff703a61afe47cf7d47a23d43b978b (34 bytes)
85+
│ └─ address: bc1pfaem24snn84nj766ksf7pyxvp3y07up6vxh7gl8h63az84pmj79sehcc5d
86+
├─ output_5: None
87+
│ ├─ value: 900u64
88+
│ ├─ script_pubkey: a9141e490a2a3641ffccc557aecddca67104f42978b387 (23 bytes)
89+
│ └─ address: 34T9hBmUH9m7aCiJzk3BCPuLaGdGKRmWdL
90+
├─ output_6: None
91+
│ ├─ value: 900u64
92+
│ ├─ script_pubkey: a914954288203f9697e25eaecb48d85b8a8608c385cf87 (23 bytes)
93+
│ └─ address: 3FJEJqGMWCA8XUnK1jypEy7bu74YMCT8eE
94+
└─ output_7: None
8395
├─ value: 900u64
84-
├─ script_pubkey: 51204f73b5561399eb397b5ab413e090cc0c48ff703a61afe47cf7d47a23d43b978b (34 bytes)
85-
└─ address: bc1pfaem24snn84nj766ksf7pyxvp3y07up6vxh7gl8h63az84pmj79sehcc5d
96+
└─ script_pubkey: 6a0f736574656320617374726f6e6f6d79 (17 bytes)

packages/wasm-utxo/js/README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ type-safe API over the raw WASM bindings.
1717

1818
- Generated by `wasm-bindgen` from Rust code
1919
- Exports classes with static methods (e.g., `AddressNamespace`, `UtxolibCompatNamespace`)
20+
- Uses `snake_case` naming (Rust convention)
2021
- Uses loose types (`any`, `string | null`) due to WASM-bindgen limitations
2122

2223
2. **Namespace Wrapper Files** (e.g., `address.ts`, `utxolibCompat.ts`, `fixedScriptWallet.ts`)
2324

2425
- Import the generated WASM namespace classes
2526
- Define precise TypeScript types to replace `any` types
2627
- Export individual functions that wrap the static WASM methods
28+
- Convert `snake_case` WASM methods to `camelCase` (JavaScript convention)
2729
- Re-export related types for convenience
2830

2931
3. **Shared Type Files** (e.g., `coinName.ts`, `triple.ts`)
@@ -42,8 +44,9 @@ type-safe API over the raw WASM bindings.
4244
Given a WASM-generated class:
4345

4446
```typescript
45-
// wasm/wasm_utxo.d.ts (generated)
47+
// wasm/wasm_utxo.d.ts (generated by wasm-bindgen)
4648
export class AddressNamespace {
49+
// Note: snake_case naming from Rust
4750
static to_output_script_with_coin(address: string, coin: string): Uint8Array;
4851
static from_output_script_with_coin(
4952
script: Uint8Array,
@@ -53,7 +56,7 @@ export class AddressNamespace {
5356
}
5457
```
5558

56-
We create a wrapper module:
59+
We create a wrapper module that provides better types and camelCase naming:
5760

5861
```typescript
5962
// address.ts
@@ -62,7 +65,9 @@ import type { CoinName } from "./coinName";
6265

6366
export type AddressFormat = "default" | "cashaddr";
6467

68+
// Wrapper provides camelCase naming and precise types
6569
export function toOutputScriptWithCoin(address: string, coin: CoinName): Uint8Array {
70+
// Calls snake_case WASM method
6671
return AddressNamespace.to_output_script_with_coin(address, coin);
6772
}
6873

@@ -71,6 +76,7 @@ export function fromOutputScriptWithCoin(
7176
coin: CoinName,
7277
format?: AddressFormat,
7378
): string {
79+
// Calls snake_case WASM method
7480
return AddressNamespace.from_output_script_with_coin(script, coin, format);
7581
}
7682
```
@@ -85,5 +91,7 @@ export * as address from "./address";
8591
### Benefits
8692

8793
- **Type Safety**: Replace loose `any` and `string` types with precise union types
88-
- **Better DX**: IDE autocomplete works better with concrete types
94+
- **Idiomatic Naming**: Each layer uses its native convention (`snake_case` in Rust, `camelCase` in JavaScript)
95+
- **Better DX**: IDE autocomplete works better with concrete types and familiar naming
8996
- **Maintainability**: Centralized type definitions prevent duplication
97+
- **Clear Separation**: WASM bindings stay pure to Rust conventions, TypeScript handles JS conventions

packages/wasm-utxo/js/fixedScriptWallet.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,17 @@ type ReplayProtection =
5757
export type ScriptId = { chain: number; index: number };
5858

5959
export type ParsedInput = {
60-
address?: string;
60+
address: string;
6161
script: Uint8Array;
6262
value: bigint;
63-
scriptId: ScriptId | undefined;
63+
scriptId: ScriptId | null;
6464
};
6565

6666
export type ParsedOutput = {
67-
address?: string;
67+
address: string | null;
6868
script: Uint8Array;
6969
value: bigint;
70-
scriptId?: ScriptId;
70+
scriptId: ScriptId | null;
7171
};
7272

7373
export type ParsedTransaction = {
@@ -90,7 +90,7 @@ export class BitGoPsbt {
9090
* @returns A BitGoPsbt instance
9191
*/
9292
static fromBytes(bytes: Uint8Array, network: NetworkName): BitGoPsbt {
93-
const wasm = WasmBitGoPsbt.fromBytes(bytes, network);
93+
const wasm = WasmBitGoPsbt.from_bytes(bytes, network);
9494
return new BitGoPsbt(wasm);
9595
}
9696

@@ -104,6 +104,21 @@ export class BitGoPsbt {
104104
walletKeys: WalletKeys,
105105
replayProtection: ReplayProtection,
106106
): ParsedTransaction {
107-
return this.wasm.parseTransactionWithWalletKeys(walletKeys, replayProtection);
107+
return this.wasm.parse_transaction_with_wallet_keys(walletKeys, replayProtection);
108+
}
109+
110+
/**
111+
* Parse outputs with wallet keys to identify which outputs belong to a wallet
112+
* with the given wallet keys.
113+
*
114+
* This is useful in cases where we want to identify outputs that belong to a different
115+
* wallet than the inputs.
116+
*
117+
* @param walletKeys - The wallet keys to use for identification
118+
* @returns Array of parsed outputs
119+
* @note This method does NOT validate wallet inputs. It only parses outputs.
120+
*/
121+
parseOutputsWithWalletKeys(walletKeys: WalletKeys): ParsedOutput[] {
122+
return this.wasm.parse_outputs_with_wallet_keys(walletKeys);
108123
}
109124
}

0 commit comments

Comments
 (0)