Issues with Schnorr Signature Verification for P2TR

2 replies 95 views
Posts: 1 · Reputation: 30
#1Mar 10, 2024, 04:44 PM
I'm trying to make a schnorr adaptor signature for a p2tr pay to script transaction based on a message hash. Here’s what I want to do: 1. Generate an adaptor signature using the oracle's private key along with Alice's x-only public key. 2. Finish the adaptor signature using Alice's private key. So far, I've managed to get these results: Lender x-only pubkey: 35dfcbda618c2f3386eb523acc6087d44137fa3803416a40b849c877c0f14562 Oracle x-only pubkey: 6bee714f4df6d3a3a3cb45ea623876359c49676e00274473968adb3c3a18e889 Message hash: ebf6e6f7d50b43019dacf6ae0ae19902784314de0a9569b35ac890264a1382c6 Completed sig: ddd56d569940d52555f95dc4e0dc88e10ec5d8d2a36f5e9dc251b186dd7bd3a658155e73650a079 7b5d475f8b23d7d88bc229256a5c3f84f37075baf22209b81 But when I try to publish the transaction, it fails because of an invalid schnorr signature. Even the schnorr verification in Node.js isn't working either. Am I overlooking something in the adaptor signature process?
3 Reply Quote Share
GrimMoonMember
Posts: 17 · Reputation: 163
#2Mar 10, 2024, 09:42 PM
Looks like you’re trying to verify your adaptor signature (named s)  with the lender’s public key as you would validate a regular signature. Validate the adaptor signature manually by multiplying the plain signature ‘s_hat’ with G, and adding the oracles public key , which I’ll call T. When programming try to give your variables clearer names. I also find it easier to work with when people follow the convention of lower casing scalars.As a disclaimer,  I didn’t run the code, I just skimmed it.
5 Reply Quote Share
paul.ninjaFull Member
Posts: 152 · Reputation: 539
#3Mar 11, 2024, 02:11 AM
A few gotchas that usually break Schnorr adaptor sigs for P2TR: Verify the right thing A pre-signature will NOT verify with a normal Schnorr verifier and the lender's pubkey. Public check for adaptor pre-sig is: s_hatG = R_hat + eQ + Y where Y = t*G (oracle/adaptor pubkey), Q is the signing pubkey, and e = H_bip340(x(R_hat+Y) || x(Q) || m). Note e uses R_final = R_hat + Y, not R_hat.Taproot key vs script key Key-path spend: Q must be the TWEAKED key (Q = P + H_tapTweak(P, merkle_root)*G) and the secret used to sign is x' = x + H_tapTweak(...). If you verify against the untweaked pubkey, it fails. Script-path spend (OP_CHECKSIG in tapscript): use the script's x-only pubkey directly (no tap tweak). Make sure your sighash is BIP341 for script path. Even-y convention and completion. BIP340 requires even y for the final R. Choose R_hat so that y(R_hat + Y) is even; if not, set R_hat := -R_hat. Completion rule: if y(R_hat + Y) is even: s = s_hat + t else: s = s_hat - t (mod n) Final signature is (x(R_hat+Y), s) and should verify with a standard BIP340 verifier against Q. Sighash and libs The message must be the exact BIP341 sighash for your spend (key-path vs script-path differ). A random 32-byte hash won't validate on-chain. libsecp256k1 has ECDSA adaptor support; for Schnorr you're rolling your own math. Keep the variable names straight (lowercase scalars, uppercase points) and unit-test each equation above. Compute correct Q (tweaked or not). Pick R_hat, adjust for parity, compute e with x(R_hat+Y). s_hat = k + e*x' (mod n). Check s_hat relation above. Complete with ±t, build final (R', s), verify with standard schnorr against Q.
4 Reply Quote Share

Related topics