Skip to content

RFC: a WitnessTx abstraction to unblock RGB on Liquid (207-line non-breaking patch, working PoC) #12

Description

@bitwalt

The ask

RGB consensus already names Liquid: Layer1::Liquid, ChainNet::LiquidMainnet/LiquidTestnet, and the chain_net field on Genesis. The crypto layer (mpc, dbc::tapret, dbc::opret) is already chain-agnostic — a P2TR scriptPubKey is byte-identical on Bitcoin and Elements.

The one thing that still blocks RGB-on-Liquid is that the witness-verification path is typed against bitcoin::Transaction, which cannot deserialize an Elements transaction. We propose abstracting that one type behind a small WitnessTx trait. The patch is written and validated; we'd like maintainer input on its shape before opening a PR.

Full RFC: https://github.com/kaleidoswap/rgb-on-liquid-spike/blob/main/RFC.md

The gap

The Bitcoin-tx coupling in the verification path is exactly three spots — dbc/proof.rs (Proof::verify), dbc/anchor.rs (Anchor::verify), and seals/txout/witness.rs (Witness<D>.tx). Everything those paths need from the transaction is small: the input outpoints (seal-closure check) and the output scriptPubKeys (tapret/opret recovery). No witness data, no fees, no signatures.

Proposed change

A three-method trait, implemented for bitcoin::Transaction so existing callers are unaffected:

pub trait WitnessTx {
    fn witness_txid(&self) -> [u8; 32];
    fn input_outpoints(&self) -> Vec<([u8; 32], u32)>;
    fn output_script_pubkeys(&self) -> Vec<Vec<u8>>;
}

dbc::Proof::verify and Anchor::verify become generic over W: WitnessTx; the concrete TapretProof/OpretProof impls iterate output_script_pubkeys() and dispatch to the existing ScriptBuf-level paths, which were already chain-agnostic.

Evidence

Applied to a vendored rgb-consensus 0.11.1-rc.10 and run end-to-end on Bitcoin + Liquid regtest:

Result
Patch size 207 LOC, 7 files (+1 new)
Upstream test suite on the patched crate 45/45 pass, none modified
Breaking changes for bitcoin::Transaction callers none
rgb-ops, rgb-schemas, rgb-invoicing, rgb-aluvm compile + run unchanged
Liquid-side adapter (impl WitnessTx for elements::Transaction) ~30 LOC

With the patch in place we exercised: a real RGB20 (NIA) transfer on Liquid (unmodified rgb-ops + rgb-schemas), the same transfer with fully confidential (blinded) outputs, a cross-chain Bitcoin↔Liquid RGB atomic swap over a full HTLC (CSV refund branch, negative tests against consensus), and an HTLC claim transaction that simultaneously closes the seal and carries the tapret anchor. All reproducible: https://github.com/kaleidoswap/rgb-on-liquid-spike (file-by-file patch in vendor/rgb-consensus-patched/PATCH.md).

Compatibility

The only public-surface change is dbc::Proof::verify gaining a generic parameter — a minor-version concern, no semantic change. One integration note worth documenting: TapretFirst requires the commitment to be the first P2TR output, so a Liquid wallet that auto-sorts outputs will silently break verification.

Open questions for maintainers

  1. WitnessTx location — inside rgb-consensus (our preference) or a sibling crate?
  2. Trait shape — are the three methods the right minimum, or should version/locktime be exposed for future proof types?
  3. Witness<D> struct — make its tx field generic (Witness<D, T = bitcoin::Transaction>), or add a generic sibling?

What we will do

KaleidoSwap will write the PR against whichever shape you prefer, maintain a WitnessTx impl for elements::Transaction in a sibling repo so integrators have a drop-in path, and ship regression tests plus the working demos with the PR.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions