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?
Looks like youre trying to verify your adaptor signature (named s) with the lenders 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 Ill 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 didnt run the code, I just skimmed it.
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.