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
WitnessTx location — inside rgb-consensus (our preference) or a sibling crate?
- Trait shape — are the three methods the right minimum, or should
version/locktime be exposed for future proof types?
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.
The ask
RGB consensus already names Liquid:
Layer1::Liquid,ChainNet::LiquidMainnet/LiquidTestnet, and thechain_netfield onGenesis. 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 smallWitnessTxtrait. 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), andseals/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::Transactionso existing callers are unaffected:dbc::Proof::verifyandAnchor::verifybecome generic overW: WitnessTx; the concreteTapretProof/OpretProofimpls iterateoutput_script_pubkeys()and dispatch to the existingScriptBuf-level paths, which were already chain-agnostic.Evidence
Applied to a vendored
rgb-consensus0.11.1-rc.10 and run end-to-end on Bitcoin + Liquid regtest:bitcoin::Transactioncallersrgb-ops,rgb-schemas,rgb-invoicing,rgb-aluvmimpl WitnessTxforelements::Transaction)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 invendor/rgb-consensus-patched/PATCH.md).Compatibility
The only public-surface change is
dbc::Proof::verifygaining a generic parameter — a minor-version concern, no semantic change. One integration note worth documenting:TapretFirstrequires the commitment to be the first P2TR output, so a Liquid wallet that auto-sorts outputs will silently break verification.Open questions for maintainers
WitnessTxlocation — insidergb-consensus(our preference) or a sibling crate?version/locktimebe exposed for future proof types?Witness<D>struct — make itstxfield 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
WitnessTximpl forelements::Transactionin a sibling repo so integrators have a drop-in path, and ship regression tests plus the working demos with the PR.