fix(utxo-lib): honor caller hashType for SmartTransaction signatures (atomic-swap offers)#6
Open
caribu66 wants to merge 1 commit into
Conversation
TransactionBuilder produced and validated SmartTransaction (identity/CC) signatures as if they were always SIGHASH_ALL, which broke atomic-swap offers signed SIGHASH_SINGLE|ANYONECANPAY: 1. sign(): the SmartTransactionSignatures blob hardcoded sigHashType=1 while the digest was computed with the caller-supplied hashType, so any non-ALL smart signature verified against the wrong digest and the completed tx was rejected (mandatory-script-verify-flag / false-empty-stack). 2. __canModifyInputs / __canModifyOutputs read the hashtype from the trailing byte of a DER signature, which is meaningless for a SmartTransactionSignatures blob. Adding a taker's inputs/outputs to a maker-signed offer therefore threw "No, this would invalidate signatures". Both guards now parse the blob header via SmartTransactionSignatures.fromChunk: ANYONECANPAY permits extra inputs, and SIGHASH_SINGLE permits appending outputs once the paired (same-index) output exists. Proven live on VRSCTEST: full self-custodial atomic swap (maker offer signed SINGLE|ANYONECANPAY, taker completes) settles and the identity transfers with revocation/recovery authorities preserved.
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.
Summary
TransactionBuildertreated SmartTransaction (VerusID / CryptoCondition) signatures as if they were alwaysSIGHASH_ALL, which made atomic-swap offers — signedSIGHASH_SINGLE | ANYONECANPAYso a taker can complete them — impossible to build or verify. This fixes both the signing and the mutability guards.1.
sign()— declare the real hashtype in the blobThe
SmartTransactionSignaturesblob hardcodedsigHashType = 1while the digest was computed with the caller-suppliedhashType. Any non-ALL smart signature therefore verified against the wrong digest and the completed transaction was rejected (mandatory-script-verify-flag-failed, false/empty top stack).2.
__canModifyInputs/__canModifyOutputs— read the blob header, not a DER byteBoth guards derived the hashtype from the trailing byte of a DER signature, which is meaningless for a
SmartTransactionSignaturesblob. As a result, adding a takers inputs/outputs to a maker-signed offer threwNo, this would invalidate signatures. They now parse the blob header viaSmartTransactionSignatures.fromChunk:ANYONECANPAYpermits appending more inputs.SIGHASH_SINGLEpermits appending outputs once the paired (same-index) output exists (vin < nOutputs);SIGHASH_NONEalways permits.Applied to both
src/anddist/src/.Why it matters
This is what lets a wallet act as an atomic-swap taker entirely client-side: keep the makers
SINGLE|ANYONECANPAYidentity input untouched, add the buyers funding inputs + the transferred identity output + change, sign only the buyer inputs, and broadcast.Testing
Proven live on VRSCTEST via the Verus Mobile marketplace flows: a maker offer signed
SINGLE|ANYONECANPAYis completed by a taker on a second device; the swap settles on-chain and the identity transfers to the buyer with revocation/recovery authorities preserved. Two independent on-device swaps across two phones passed first-try.Downstream (Verus-Mobile) currently carries this as a
patch-packagepatch; merging here lets that patch be dropped.