feat(intent): set BIP-322 PSBT_GLOBAL_GENERIC_SIGNED_MESSAGE (0x09) - #252
feat(intent): set BIP-322 PSBT_GLOBAL_GENERIC_SIGNED_MESSAGE (0x09)#252afsheenb wants to merge 2 commits into
Conversation
Intent-proof PSBTs never set BIP-322's global 0x09 field (the encoded message being signed). Without it a co-signer cannot recompute the to_spend commitment from PSBT-internal data alone, making it impossible to reliably distinguish a genuine ownership proof from an ordinary fund-moving spend before contributing a partial signature. Insert the 0x09 global field (type_value=0x09, key=empty) into the proof PSBT inside build_proof_psbt(), using the already-encoded message string that is computed there for the to_spend tagged hash. The field round-trips correctly through PSBT serialize/deserialize. Add two unit tests: one verifying the field is set to the expected bytes, one verifying it survives a PSBT round-trip.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Walkthrough
ChangesBIP-322 PSBT Global Field
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ark-core/src/intent.rs`:
- Around line 636-647: The dummy_input fixture in intent::dummy_input is
inconsistent with its contract: the comment says it should be built from an
OP_TRUE leaf, but the leaf is created with ScriptBuf::new_op_return. Update
dummy_input to construct a trivially spendable tapscript that matches the stated
OP_TRUE behavior, or adjust the helper comment to reflect the actual leaf type
so tests don’t rely on an unspendable fixture.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0a97eb06-ca1a-4efc-9cf3-19b97705956f
📒 Files selected for processing (1)
ark-core/src/intent.rs
- Add /// doc comments to all top-level public items (Input, Intent, make_intent, IntentMessage and their key methods) to bring CodeRabbit docstring coverage above the 80% threshold. - Fix dummy_input: leaf was built with ScriptBuf::new_op_return which is unspendable and contradicted the "OP_TRUE leaf" comment; replace with ScriptBuf::builder().push_opcode(OP_TRUE).into_script() so the fixture matches its stated contract.
|
@coderabbitai I've updated the docstrings and fixed the test snafu. All better? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Outline =="
ast-grep outline ark-core/src/intent.rs --view expanded || true
echo
echo "== Current diff for ark-core/src/intent.rs =="
git diff -- ark-core/src/intent.rs || true
echo
echo "== Relevant file slices =="
nl -ba ark-core/src/intent.rs | sed -n '1,260p'
echo
nl -ba ark-core/src/intent.rs | sed -n '260,520p'Length of output: 3067 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Public items and surrounding doc comments =="
cat -n ark-core/src/intent.rs | sed -n '30,210p'
echo
echo "== build_proof_psbt and IntentMessage area =="
cat -n ark-core/src/intent.rs | sed -n '300,520p'
echo
echo "== tests / dummy_input =="
cat -n ark-core/src/intent.rs | sed -n '642,780p'Length of output: 21140
The public-facing docs I was worried about are now in place, and the test helper is consistent again: No further concerns from my side on those points. If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! |
arkana-ai-bot
left a comment
There was a problem hiding this comment.
Reviewed ark-core/src/intent.rs at 720e040. Small, focused change; behavior is metadata-only (no consensus/signing effect).
Correctness of the 0x09 insertion
psbt.unknown.insert(Key { type_value: 0x09, key: vec![] }, message.as_bytes().to_vec())at ark-core/src/intent.rs:414-417 is placed after the per-input loop, so it runs exactly once. Good.- The bytes match the JSON-encoded message string already used to build the to_spend tagged hash (
messageshadowed frommessage.encode()?at intent.rs:330-333), so the field and the commitment are consistent. - In rust-bitcoin 0.32.7 (per ark-core/Cargo.toml:17), PSBT global key type 0x09 is unused, so it round-trips through
unknown. Thebuild_proof_psbt_0x09_survives_psbt_roundtriptest confirms this.
Cross-repo interop check
- ts-sdk sets the same field:
packages/ts-sdk/src/intent/index.ts:160,314-330(const PSBT_GLOBAL_GENERIC_SIGNED_MESSAGE = 0x09, empty key, UTF-8 message). - arkd writes it too:
pkg/ark-lib/intent/proof.go:158-163(Unknowns = append(..., &psbt.Unknown{Key: []byte{0x09}, Value: []byte(message)})). btcsuiteUnknown.Keyis the raw key bytes (type byte only, no extra key data), which serializes to length-prefix + 0x09 — byte-identical to rust-bitcoin'sKey { type_value: 0x09, key: vec![] }. Interop with the Go implementation is intact.
Server-side consumption — none, and that's fine
arkd/pkg/ark-lib/intent/proof.go'sVerify(proofB64, message, skip)takes the message as an out-of-band parameter (proof.go:57-87) and never reads the 0x09 field. So this is purely informational metadata for verifiers that don't already have the message; the actual security binding remains the to_spend commitment insideunsigned_tx.input[0].previous_output.txid. Not protocol-critical.
Naming nit
- "BIP-322 PSBT_GLOBAL_GENERIC_SIGNED_MESSAGE (0x09)" is not something BIP-322 actually defines; it's an Ark ecosystem convention shared across the three SDKs. The naming is copied consistently, so no action needed here — but if a future BIP allocates 0x09 as a real global type, older PSBTs will fail to deserialize on newer library versions. Same risk carried by ts-sdk and arkd; worth being aware of.
Tests
- Danger's "no test changes" warning is stale: two new tests are added (intent.rs:743-767) covering both the initial insertion and the serialize/deserialize round-trip.
dummy_input()correctly builds a taproot input using G's x-coordinate as the internal key, a single OP_TRUE leaf at depth 0, and matching control block — sufficient for the metadata check. OnlyIntentMessage::Deleteis exercised, but since 0x09 handling doesn't branch on the variant, that's acceptable. - No test asserts that
make_intent()(the public wrapper) also propagates the field. It does today because it doesn't mutatepsbt.unknown, but a light regression guard there wouldn't hurt if you want defense in depth.
Doc comments
- The doc additions on
Input,Input::new,Intent,Intent::new,Intent::serialize_proof,Intent::serialize_message,make_intent, andIntentMessage/IntentMessage::encodeare accurate. No API surface changes.
No blockers. LGTM.
Intent-proof PSBTs never set BIP-322's global 0x09 field (the encoded message being signed). Without it a co-signer cannot recompute the to_spend commitment from PSBT-internal data alone, making it impossible to reliably distinguish a genuine ownership proof from an ordinary fund-moving spend before contributing a partial signature.
Insert the 0x09 global field (type_value=0x09, key=empty) into the proof PSBT inside build_proof_psbt(), using the already-encoded message string that is computed there for the to_spend tagged hash. The field round-trips correctly through PSBT serialize/deserialize.
Add two unit tests: one verifying the field is set to the expected bytes, one verifying it survives a PSBT round-trip. Provides feature parity with arkade-os/ts-sdk#578.
Summary by CodeRabbit