Skip to content

pset: sign an issuing input over its own outpoint and denomination - #31

Merged
GracedEternalKingCabbageMan merged 1 commit into
sequentiafrom
fix/pset-issuance-outpoint-denomination
Oct 3, 2026
Merged

GracedEternalKingCabbageMan merged 1 commit into
sequentiafrom
fix/pset-issuance-outpoint-denomination

Conversation

@GracedEternalKingCabbageMan

Copy link
Copy Markdown
Collaborator

A caller that holds a whole transaction signs it by making a PSET from it (PartiallySignedTransaction::from_tx). For an input that issues an asset, that PSET extracted a different transaction from the one given, so the signature covered the wrong hash. The Arca server worked around both defects:

  • The outpoint. Input::from_txin set bit 31 of the output index to mark the issuance. extract_tx and issuance_ids then read that index as the outpoint's, so the signed transaction spent output 2^31 + n, and the issued asset's id was computed over that outpoint.
  • The denomination. Input::asset_issuance() wrote denomination 8 whatever the issuance said. Sequentia serializes the denomination after the inflation keys, so it is part of the signature hash.

The fix:

  • The issuance is no longer flagged in the index; Elements Core's PSET does not flag it either.
  • Input::previous_outpoint() masks off any flag a PSET carries there, as upstream rust-elements 0.27 does. The extracted transaction, the asset id and the kit's readers of an input's outpoint all use it.
  • The denomination has no PSET field, so it is kept in a proprietary key (prefix sequentia, subtype 0x00, one byte). The key is written only when the denomination is not the default 8, so existing PSETs serialize exactly as before.

Each defect has a test that fails before this change:

  • rust-elements an_issuance_input_keeps_its_outpoint: the outpoint came back with vout: 2147483648 instead of 0.
  • an_issuance_input_keeps_its_denomination: the denomination came back as 8, not 0.
  • lwk_signer an_issuing_input_is_signed_over_its_own_transaction: the kit's signature did not verify against the transaction's own sighash ("signature failed verification").

On regtest (node v24.7.12, elementsregtest, -par=1), a P2WPKH coin issued an asset and was signed through the kit's PSET signer:

  • Before the fix, it was refused in the mempool (mempool-script-verify-flag-failed (Signature must be zero for failed CHECK(MULTI)SIG operation)). Forced into a block with generateblock, it was refused again (TestBlockValidity failed: mempool-script-verify-flag-failed (Script evaluated without error but finished with a false/empty top stack element)). This held at denominations 8 and 2.
  • After the fix, coins at vout 1 with denominations 8, 2 and 0 were accepted and confirmed, and the node reports each issuance's denomination as set.
  • A PSET whose denomination key is removed is still refused, with the same two errors.

Suites: rust-elements 91 tests without sequentia and 86 with it, plus 14 doc-tests; lwk_signer 26 (27 with ark); lwk_common 40; lwk_wollet ark:: 22.

A PSET made from a transaction with an issuing input (from_tx, as a
caller holding a whole transaction signs it) extracted another
transaction than the one given, so the signature covered another hash:

- Input::from_txin set bit 31 of the output index to mark the issuance,
  and extract_tx and issuance_ids read the index as the outpoint's. The
  transaction signed spent output 2^31 + n, and the issued asset's id
  was computed over it.
- Input::asset_issuance() wrote denomination 8 whatever the issuance
  said. Sequentia serializes the denomination after the inflation keys,
  so it is part of the signature hash.

The issuance is no longer flagged in the index (Elements Core's PSET
does not flag it either), and Input::previous_outpoint() masks off any
flag a PSET carries there, as upstream rust-elements 0.27 does; the
extracted transaction, the asset id and the kit's readers of an input's
outpoint use it. The denomination has no PSET field, so it is kept in a
proprietary key (prefix "sequentia", subtype 0), written only when it
is not the default 8, so existing PSETs serialize as before.

The new tests fail before this change: the outpoint came back as vout
2147483648, the denomination as 8, and the kit's signature over an
issuing input did not verify against the transaction's own sighash.
@GracedEternalKingCabbageMan
GracedEternalKingCabbageMan merged commit b1e40bd into sequentia Oct 3, 2026
11 of 17 checks passed
@GracedEternalKingCabbageMan
GracedEternalKingCabbageMan deleted the fix/pset-issuance-outpoint-denomination branch October 3, 2026 12:31
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.

1 participant