Skip to content

wasm: expose Address.blindingPubkey()#159

Closed
EvanWinget wants to merge 1 commit into
Blockstream:masterfrom
EvanWinget:wasm/address-blinding-pubkey
Closed

wasm: expose Address.blindingPubkey()#159
EvanWinget wants to merge 1 commit into
Blockstream:masterfrom
EvanWinget:wasm/address-blinding-pubkey

Conversation

@EvanWinget
Copy link
Copy Markdown

@EvanWinget EvanWinget commented May 9, 2026

PsetOutputBuilder.blindingPubkey() takes a &PublicKey, but the WASM Address type has no producer for one.

elements::Address::blinding_pubkey exposes the field publicly on the Rust side.

Callers using Wollet::send_lbtc() and other high-level builders never see this: the wallet handles blinding-pubkey extraction internally, including for self-sends. Callers who construct PSET outputs manually (e.g. multi-party signing protocols where output composition is coordinated off-chain) currently have to either decode the CT address themselves in JS or skip blinding and silently degrade to unconfidential output.

This is a small accessor that mirrors what elements::Address already exposes. The returned key is the same public data already encoded in the CT address itself, so no new privacy surface AFAICT.

Add an accessor for the blinding public key of a confidential Address.
Returns Option<PublicKey>; None for unconfidential addresses.

The underlying elements::Address exposes blinding_pubkey as a public
field — this just bridges it to JS via the existing PublicKey newtype.
Building a confidential PSET output to a known recipient requires their
blinding pubkey; without this, JS callers either re-implement
segwit/SLIP-77 decoding or work around it.
@EvanWinget
Copy link
Copy Markdown
Author

CI fails because crate::PublicKey isn't in scope without the simplicity feature (from commit 2d5d73a). Blinding pubkeys are not Simplicity-specific though, so I probably need some input on the correct solution for LWK.

Couple of options:

  1. Un-gate public_key and the Secret Key/XOnlyPublicKey it imports from simplicity
  2. Gate Address::blindingPubkey() behind simplicity to match the grouping
  3. Return Vec instead of the PublicKey wrapper

My preference is option 1, but I could understand why an alternative is preferred.

@LeoComandini
Copy link
Copy Markdown
Contributor

Hi @EvanWinget ,
can you share some detailts on how do you use LWK in your app?
we might suggest workarounds

  1. Gate Address::blindingPubkey() behind simplicity to match the grouping

Please go for this solution.
For now, the only reason we're wrapping to wasm/bindings low level functions is to make simplicity pocs easier.
However we're keeping those under the simplicity feature (experimental) since we don't want to commit to that interface yet.

In general, LWK is not a JS/py/etc wrapper for rust-elements functionalities, if you need to access those outside of experimental features you need to use rust directly (which grants you full control).

That being said, if you have a compelling use case, we might be able to tweak the interface so that you can do your flows without us having to expose internals.

@EvanWinget
Copy link
Copy Markdown
Author

Hi @LeoComandini. Thanks for this information, I'm happy to close this PR since it doesn't seem like I was using LWK in line with how it's meant to be used.

Quick context on my use case: I'm porting chia-gaming's state-channel gaming protocol to Liquid/simplicity. This enables two-player turn-based games with off-chain moves and on-chain dispute resolution. I'm building it as a PWA with a passkey-gated in-browser wallet, so everything runs in the browser.

I needed Address.blindingPubkey() in WASM because I was assembling a dual-funded channel-opening PSET in TypeScript via the lwk_wasm low-level builders. Output composition is negotiated off-chain between the two parties, with an explicit shared channel output and per-party CT-blinded change.

I'm happy to move PSET construction + blinding into the rust-elements layer and remove the dependency on the experimental low-level WASM surface entirely. This also means that I can close PR #160 which I had opened alongside this one.

I'll keep using LWK for the CT-descriptor watch-only wallet and the signer. Appreciate the guidance!

@EvanWinget EvanWinget closed this May 15, 2026
@stringhandler
Copy link
Copy Markdown
Contributor

This sounds really cool. I'm always looking for simplicity scripts in the wild to follow and add to https://github.com/stringhandler/awesome-simplicity

Please send me a link if you have a public repo

@EvanWinget
Copy link
Copy Markdown
Author

This sounds really cool. I'm always looking for simplicity scripts in the wild to follow and add to https://github.com/stringhandler/awesome-simplicity

Please send me a link if you have a public repo

The repo isn't public yet, but after I have a working e2e implementation on testnet I plan to make it public!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants