Skip to content

Commit 0b7a2e0

Browse files
Merge pull request #179 from BitGo/BTC-3049.wasm-utxo-inspect
feat(wasm-utxo)!: move inspect feature from CLI to library package
2 parents 8c472ee + 60b606f commit 0b7a2e0

27 files changed

Lines changed: 1234 additions & 593 deletions

packages/wasm-utxo/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/wasm-utxo/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ unexpected_cfgs = { level = "warn", check-cfg = [
2020
'cfg(feature, values("zebra-test"))',
2121
] }
2222

23+
[features]
24+
default = []
25+
inspect = ["dep:num-bigint", "dep:serde", "dep:serde_json", "dep:hex"]
26+
2327
[dependencies]
2428
wasm-bindgen = "0.2"
2529
js-sys = "0.3"
@@ -28,6 +32,10 @@ bech32 = "0.11"
2832
musig2 = { version = "0.3.1", default-features = false, features = ["k256"] }
2933
getrandom = { version = "0.2", features = ["js"] }
3034
pastey = "0.1"
35+
num-bigint = { version = "0.4", optional = true }
36+
serde = { version = "1.0", features = ["derive"], optional = true }
37+
serde_json = { version = "1.0", optional = true }
38+
hex = { version = "0.4", optional = true }
3139

3240
[dev-dependencies]
3341
base64 = "0.22.1"

packages/wasm-utxo/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,36 @@ Zcash support includes:
3131
- **Height-Based API**: Preferred `createEmpty()` method automatically selects correct consensus rules
3232
- **Parity Testing**: Validated against `zebra-chain` for accuracy across all network upgrades
3333

34+
## Inspect Feature
35+
36+
The `inspect` feature adds PSBT and transaction parsing into hierarchical node trees,
37+
useful for building tree-view UIs and CLI formatters.
38+
39+
It is behind a Cargo feature flag because it pulls in extra dependencies (`serde`, `serde_json`,
40+
`num-bigint`, `hex`) that are not needed for core wallet operations.
41+
42+
### Rust
43+
44+
```rust
45+
// Cargo.toml
46+
wasm-utxo = { path = ".", features = ["inspect"] }
47+
48+
// Usage
49+
use wasm_utxo::inspect::{parse_psbt_bytes_with_network, parse_tx_bytes_with_network, Node};
50+
```
51+
52+
### TypeScript
53+
54+
Available as a separate import path, not included in the main `@bitgo/wasm-utxo` entry:
55+
56+
```typescript
57+
import { parsePsbtToNode, parseTxToNode, isInspectEnabled } from "@bitgo/wasm-utxo/inspect";
58+
```
59+
60+
The published npm package includes stub implementations that return `isInspectEnabled() === false`
61+
and throw runtime errors from the parse functions. To get a working build, compile the WASM with
62+
`--features inspect` (see [`packages/webui/scripts/build-wasm.sh`](../webui/scripts/build-wasm.sh)).
63+
3464
## Building
3565

3666
### Mac

packages/wasm-utxo/cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ name = "wasm-utxo-cli"
88
path = "src/main.rs"
99

1010
[dependencies]
11-
wasm-utxo = { path = ".." }
11+
wasm-utxo = { path = "..", features = ["inspect"] }
1212
clap = { version = "4.5", features = ["derive"] }
1313
anyhow = "1.0"
1414
hex = "0.4"

packages/wasm-utxo/cli/src/address.rs

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,39 @@ use clap::Subcommand;
33
use wasm_utxo::bitcoin::Script;
44
use wasm_utxo::{from_output_script_with_network, to_output_script_with_network, Network};
55

6+
use crate::network::NetworkArg;
7+
68
#[derive(Subcommand)]
79
pub enum AddressCommand {
810
/// Decode an address to its output script (hex)
911
Decode {
1012
/// The address to decode
1113
address: String,
12-
/// Network (bitcoin, testnet, litecoin, zcash, etc.)
13-
#[arg(short, long, default_value = "bitcoin")]
14-
network: String,
14+
/// Network (btc, tbtc, ltc, bch, zec, etc.)
15+
#[arg(short, long, value_enum)]
16+
network: NetworkArg,
1517
},
1618
/// Encode an output script (hex) to an address
1719
Encode {
1820
/// Output script as hex
1921
script: String,
20-
/// Network (bitcoin, testnet, litecoin, zcash, etc.)
21-
#[arg(short, long, default_value = "bitcoin")]
22-
network: String,
22+
/// Network (btc, tbtc, ltc, bch, zec, etc.)
23+
#[arg(short, long, value_enum)]
24+
network: NetworkArg,
2325
},
2426
}
2527

2628
pub fn handle_command(command: AddressCommand) -> Result<()> {
2729
match command {
2830
AddressCommand::Decode { address, network } => {
29-
let network = parse_network(&network)?;
31+
let network: Network = network.into();
3032
let script = to_output_script_with_network(&address, network)
3133
.context("Failed to decode address")?;
3234
println!("{}", hex::encode(script.as_bytes()));
3335
Ok(())
3436
}
3537
AddressCommand::Encode { script, network } => {
36-
let network = parse_network(&network)?;
38+
let network: Network = network.into();
3739
let script_bytes =
3840
hex::decode(&script).context("Invalid hex string for output script")?;
3941
let script_obj = Script::from_bytes(&script_bytes);
@@ -44,32 +46,3 @@ pub fn handle_command(command: AddressCommand) -> Result<()> {
4446
}
4547
}
4648
}
47-
48-
fn parse_network(network: &str) -> Result<Network> {
49-
// Try utxolib name first (e.g., "bitcoin", "testnet", "bitcoincash")
50-
if let Some(net) = Network::from_utxolib_name(network) {
51-
return Ok(net);
52-
}
53-
54-
// Try coin name (e.g., "btc", "ltc", "bch")
55-
if let Some(net) = Network::from_coin_name(network) {
56-
return Ok(net);
57-
}
58-
59-
// Try common aliases
60-
let normalized = network.to_lowercase();
61-
match normalized.as_str() {
62-
"test" | "testnet3" => Ok(Network::BitcoinTestnet3),
63-
"signet" => Ok(Network::BitcoinPublicSignet),
64-
"ltctest" => Ok(Network::LitecoinTestnet),
65-
"bchtest" => Ok(Network::BitcoinCashTestnet),
66-
"bsvtest" => Ok(Network::BitcoinSVTestnet),
67-
"btgtest" => Ok(Network::BitcoinGoldTestnet),
68-
"dashtest" => Ok(Network::DashTestnet),
69-
"zectest" => Ok(Network::ZcashTestnet),
70-
"dogetest" => Ok(Network::DogecoinTestnet),
71-
"xec" => Ok(Network::Ecash),
72-
"xectest" => Ok(Network::EcashTestnet),
73-
_ => anyhow::bail!("Unknown network: {}", network),
74-
}
75-
}

packages/wasm-utxo/cli/src/format/fixtures.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#[cfg(test)]
22
use super::tree::{node_to_string_with_scheme, ColorScheme};
33
#[cfg(test)]
4-
use crate::node::Node;
5-
#[cfg(test)]
64
use std::env;
75
#[cfg(test)]
86
use std::fs;
97
#[cfg(test)]
108
use std::io::{self, Write};
9+
#[cfg(test)]
10+
use wasm_utxo::inspect::Node;
1111

1212
/// Ensure the generated tree output matches the fixture file
1313
/// If the fixture doesn't exist, it will be created

packages/wasm-utxo/cli/src/format/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::format::fixtures::assert_or_update_fixture;
2-
use crate::node::{Node, Primitive};
32
use num_bigint::BigInt;
3+
use wasm_utxo::inspect::{Node, Primitive};
44

55
#[test]
66
fn test_simple_tree() -> std::io::Result<()> {

packages/wasm-utxo/cli/src/format/tree.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use crate::node::{Node, Primitive};
21
use colored::*;
32
use ptree::print_tree;
43
#[cfg(test)]
54
use ptree::TreeBuilder;
65
use std::borrow::Cow;
76
use std::io;
7+
use wasm_utxo::inspect::{Node, Primitive};
88

99
/// Defines how different parts of the tree should be styled
1010
#[derive(Clone, Debug)]

packages/wasm-utxo/cli/src/main.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ use clap::{Parser, Subcommand};
44
mod address;
55
mod format;
66
mod input;
7-
mod node;
8-
mod parse;
7+
mod network;
98
mod psbt;
109
mod tx;
1110

11+
pub use network::NetworkArg;
12+
13+
#[cfg(test)]
14+
mod parse_tests;
1215
#[cfg(test)]
1316
pub mod test_utils;
1417

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//! Network argument type for CLI commands
2+
3+
use clap::ValueEnum;
4+
use wasm_utxo::Network;
5+
6+
/// CLI argument type for network selection
7+
#[derive(Debug, Clone, Copy, ValueEnum)]
8+
pub enum NetworkArg {
9+
Btc,
10+
Tbtc,
11+
Tbtc4,
12+
Ltc,
13+
Tltc,
14+
Bch,
15+
Tbch,
16+
Bcha,
17+
Tbcha,
18+
Btg,
19+
Tbtg,
20+
Bsv,
21+
Tbsv,
22+
Dash,
23+
Tdash,
24+
Doge,
25+
Tdoge,
26+
Zec,
27+
Tzec,
28+
}
29+
30+
impl From<NetworkArg> for Network {
31+
fn from(arg: NetworkArg) -> Self {
32+
match arg {
33+
NetworkArg::Btc => Network::Bitcoin,
34+
NetworkArg::Tbtc => Network::BitcoinTestnet3,
35+
NetworkArg::Tbtc4 => Network::BitcoinTestnet4,
36+
NetworkArg::Ltc => Network::Litecoin,
37+
NetworkArg::Tltc => Network::LitecoinTestnet,
38+
NetworkArg::Bch => Network::BitcoinCash,
39+
NetworkArg::Tbch => Network::BitcoinCashTestnet,
40+
NetworkArg::Bcha => Network::Ecash,
41+
NetworkArg::Tbcha => Network::EcashTestnet,
42+
NetworkArg::Btg => Network::BitcoinGold,
43+
NetworkArg::Tbtg => Network::BitcoinGoldTestnet,
44+
NetworkArg::Bsv => Network::BitcoinSV,
45+
NetworkArg::Tbsv => Network::BitcoinSVTestnet,
46+
NetworkArg::Dash => Network::Dash,
47+
NetworkArg::Tdash => Network::DashTestnet,
48+
NetworkArg::Doge => Network::Dogecoin,
49+
NetworkArg::Tdoge => Network::DogecoinTestnet,
50+
NetworkArg::Zec => Network::Zcash,
51+
NetworkArg::Tzec => Network::ZcashTestnet,
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)