Custom payouts for mediation part 2: add tx builder & RPC endpoints#144
Merged
ChrisSon15 merged 6 commits intobisq-network:mainfrom Apr 6, 2026
Merged
Conversation
Also remove an unnecessary "anyhow::.." qualification that wasn't picked up by rustc (even after enabling the lint) for some reason, and revert the formatting of "container.args(..)" to its pre-rustfmt-normalised version, adding '#[rustfmt::skip]', to improve readability. (Both in 'testenv/src/lib.rs'.)
Run "cargo update" to bump the minor/patch versions of all the project dependencies to latest, making sure that the minimum versions specified in the manifests match those in the lockfile. Also tidy them slightly. Make major/breaking version updates of 'simple-semaphore' to 1.0.0 and 'zeromq' to 0.5, which require no code changes. But hold 'hmac' & 'sha2' at 0.12 & 0.10 respectively, to avoid bloating the build, as 'musig2' still specifies the old versions of those two dependencies.
Use a helper fn to ensure that all the Deposit Tx inputs are actually signed, and all have empty ScriptSigs, when extracting the final signed tx from the builder's PSBT, since 'Psbt::extract_tx' does not do this. No attempt is made to validate the final witnesses, just ensure that no more can be added. The fact that 'extract_tx' consumes the PSBT (which requires us to clone it) suggests that this method wasn't being used as intended, since the supplied PSBT could have further witnesses added, leading to different extracted txs. Thus, in cases where the supplied PSBT isn't consumed, it should first be checked to make sure that all inputs are fully signed and finalised, to ensure a robust API. Also remove a redundant validation TODO, when merging the peer's PSBT, as it looks from the code that 'Psbt::combine' should be perfectly safe to use as-is, without further sanity checking.
Provide a new tx builder for signed Custom Payouts from mediated trades,
together with a unit test to show that the construction & signing works
without error, and that the final signed tx has the expected weight. As
with the Deposit Tx builder, the (un)signed tx is held in a PSBT.
Since the trade wallet is responsible for the multisig signing, the PSBT
needs to be augmented with extra data to allow the wallet to determine
the script path and key to sign with, and thus the 'witness_utxo' and
'tap_(internal_key|merkle_root|key_origins|scripts)' input fields must
be populated. To this end, provide '(buyer|seller)_input_descriptor' tx
builder params in addition to the regular '(buyer|seller)_input' params,
along with a new 'script_paths::deposit_payout_descriptor' fn to supply
the builder with (definite) input descriptors of the form,
tr({internal_key},and_v(v:pk({buyer_pub_key}),pk({seller_pub_key})))
which the PSBT inputs are updated from when the unsigned tx is computed.
For now, use the fake wallet in 'bdk_wallet::test_utils' with a single
static private key, in place of the mock trade wallet, when unit testing
the Custom Payout signing.
TODO: Get Custom Payout signing working with the mock trade wallet.
TODO: Ensure signing still works with xprvs in place of single keys.
Provide 'MockTradeWallet' with support to script-sign a PSBT whose inputs have a single tapscript path, in addition to the existing mock keysigning of selected inputs. This should be suitable to sign Custom Payout and dynamic (non-prepared) Claim Txs, provided their PSBT inputs have been updated from a descriptor to include the 'tap_key_origins' & 'tap_scripts' fields needed to locate the control block, script and leaf hash of the path, as well as the mapping from pubkeys to leaf hashes. To this end, add a 'script_sigs' field to 'MockTradeWallet', defining a map from pubkeys to a vector of signatures to use. Whenever a script- spending PSBT input with matching pubkey is encountered, a signature is popped from the vector to add to the 'tap_script_sigs' field of that input, and the input is automatically mock-finalised once enough signatures have been added. Furnish 'psbt::mock_(buyer|seller)_trade_wallet' with the signatures and internal keys needed to exactly match the signing behaviour of the fake BDK wallets in 'transaction::tests::test_custom_payout_tx_builder', and update the unit test to verify that. This will allow 'rpc::protocol' to create and sign Custom Payout Txs (with bogus hardcoded signatures) for the 'Musig' gRPC service, which still uses mock trade wallets. (TODO: Implement that.)
Provide 'rpc::protocol' API calls to build and sign a Custom Payout Tx, combining PSBTs exchanged with the peer, for mediated trade closures. Add a 'custom_payout_tx' field to 'TradeModel', with associated wrapper struct holding a single 'CustomPayoutTxBuilder', partially populated during trade setup. Currently, this allows just a single-shot mediated trade closure (since the builder fields are write-once), with the two output addresses of the Custom Payout Tx set to the traders' respective Claim payout addresses, which are decided at the trade start. Add 'Musig' gRPC endpoints: 'SignCustomPayoutTx' to create and partially sign a Custom Payout PSBT from the agreed seller's payout amount and tx feerate, and 'CustomCloseTrade' to merge the peer's PSBT and broadcast the resulting signed tx. Also add a test case to 'TradeProtocolClient.java' to show how to make a mediated trade closure with the updated API.
Collaborator
|
ACk |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Provide a new tx builder for signed Custom Payouts from mediated trades, together with a unit test to show that the construction & signing works without error, and that the final signed tx has the expected weight. As with the Deposit Tx builder, the (un)signed tx is held in a PSBT.
Since the trade wallet is responsible for the multisig signing, the PSBT needs to be augmented with extra data to allow the wallet to determine the script path and key to sign with, and thus we update the PSBT with (definite) descriptors for the buyer/seller inputs, when the unsigned tx is computed. The descriptor is supplied by the function
script_paths::deposit_payout_descriptorand is of the formWhen both signatures have been added to the Custom Payout PSBT, it is automatically finalised so that a signed tx can be extracted. This requires some slight changes to the
MemWalletand BDKWalletimpls ofTradeWallet. The mock trade wallet has also been updated (along with with new fake hardcoded signatures for the buyer/seller mock instances to use) to match the behaviour of the real wallets when partially signing the Custom Payout PSBT.Provide the new
MusigRPC endpoints:SignCustomPayoutTxto create and partially sign a Custom Payout PSBT from the agreed seller's payout amount and tx feerate, andCustomCloseTradeto merge the peer's PSBT and broadcast the resulting signed tx. (For now, the service uses a mock trade wallet and does not do a real broadcast of any of the protocol txs.) Provide new API calls torpc::protocolto implement the construction, partial signing & merging of Custom Payout PSBTs, using the new tx builder.Also provide a test case to
TradeProtocolClient.javato show how to use the new RPC endpoints.NOTE: Since only a single
CustomPayoutTxBuideris held in the trade model and the builder's fields are write-once, the mediated payouts provided in this way are currently single-shot only and don't permit readjustment of the tx fee or payout amounts/addresses.--
Also tidy the formatting of assorted Rust source files a little (mainly whitespace & import list order), as well as the Cargo manifests, and run
cargo updateto bump all dependencies to latest (excludingbdk_wallet,rand,rand_chacha,rusqlite,hmac&sha2, which are held back for compatibility reasons or to avoid bloating the build).--
Since this PR is already quite big, I will probably provide integration tests for the custom payouts in a third, final PR. This may require further changes to the
TradeWalletimpls to make sure they work for wallets with an actual keychain from an xprv instead of a single private key.--
TODO: Try to make
script_paths::deposit_payout_descriptora bit more robust and panic-free, instead of just parsing a formatted string as it does currently. Also make sure dust payout amounts are handled properly. (I will probably do both these things in the final PR for custom payouts.)