Difference between revisions of "Pay server"
| (One intermediate revision by the same user not shown) | |||
| Line 1: | Line 1: | ||
| − | '''Signing Flow''' | + | == '''Signing Flow''' == |
| + | |||
| + | |||
Right now the flow is: | Right now the flow is: | ||
| Line 5: | Line 7: | ||
#payserver adds input funding + change output, and signs them, sends back to client | #payserver adds input funding + change output, and signs them, sends back to client | ||
#client finally signs their input, and broadcasts | #client finally signs their input, and broadcasts | ||
| − | If we want payserver to handle the broadcast itself right after signing for funding, we would need to adapt the flow | + | If we want payserver to handle the broadcast itself right after signing for funding, we would need to adapt the flow (but maybe this would introduce concurrency issues since no signing on first ping would not activate _pendingUtxos array in payserver purse, so we would need to add a new management for reserved utxos + timeout, and this would add one more round-trip so 200-800 ms of latency) |
| + | |||
| + | note: the only reason i'm considering this 2nd flow is bc i thought it might reduce race conditions, but since every run instance already manages its own internal queue for utxos & signing, it seems useless. Just make sure to have a different purse on each server (for ex if you have multiple payservers). | ||
| + | |||
| + | New flow would be: | ||
#client builds, no funding, no sign, sends to payserver | #client builds, no funding, no sign, sends to payserver | ||
#payserver adds input funding + change output, no signing, sends back to client | #payserver adds input funding + change output, no signing, sends back to client | ||
#client signs their input, sends to payserver again | #client signs their input, sends to payserver again | ||
| − | #payserver signs their funding input + | + | #payserver signs their funding input + broadcast (ex with /finalize endpoint) |
| − | + | <h2 class="mwt-heading" >'''Pourquoi pay() doit avoir lieu avant les autres signatures'''</h2> | |
| − | < | ||
<p>'''Engagement de signature''' : Les signatures Bitcoin hachent l’intégralité de la structure de la transaction (inputs, outputs, montants, scripts). Modifier quoi que ce soit après la signature invalide les signatures (le hash change).</p> | <p>'''Engagement de signature''' : Les signatures Bitcoin hachent l’intégralité de la structure de la transaction (inputs, outputs, montants, scripts). Modifier quoi que ce soit après la signature invalide les signatures (le hash change).</p> | ||
<p>Dans les transactions multi-parties :<br>La fonction <code>pay()</code> modifie la transaction (elle ajoute des inputs/outputs pour les frais) → elle '''doit''' donc intervenir '''avant''' les signatures finales sur ces nouvelles parties.</p> | <p>Dans les transactions multi-parties :<br>La fonction <code>pay()</code> modifie la transaction (elle ajoute des inputs/outputs pour les frais) → elle '''doit''' donc intervenir '''avant''' les signatures finales sur ces nouvelles parties.</p> | ||
<p>Si une partie signe en premier, puis que <code>pay()</code> ajoute des inputs → les signatures sont cassées (elles s’étaient engagées sur l’ancien hash).</p> | <p>Si une partie signe en premier, puis que <code>pay()</code> ajoute des inputs → les signatures sont cassées (elles s’étaient engagées sur l’ancien hash).</p> | ||
| + | <h3 class="mwt-heading" >Pourquoi ne pas utiliser '''SIGHASH_ANYONECANPAY ?'''</h3> | ||
<p>'''SIGHASH_ANYONECANPAY''' permettrait théoriquement au client de signer '''avant''' le <code>pay()</code>, mais voici pourquoi '''Run n’utilise pas SIGHASH_ANYONECANPAY''' dans son code par défaut :</p> | <p>'''SIGHASH_ANYONECANPAY''' permettrait théoriquement au client de signer '''avant''' le <code>pay()</code>, mais voici pourquoi '''Run n’utilise pas SIGHASH_ANYONECANPAY''' dans son code par défaut :</p> | ||
<p>Le protocole '''Run''' (avant RunCraft) utilise exclusivement '''SIGHASH_ALL''' pour garantir l’intégrité des jigs.</p> | <p>Le protocole '''Run''' (avant RunCraft) utilise exclusivement '''SIGHASH_ALL''' pour garantir l’intégrité des jigs.</p> | ||
Latest revision as of 20:57, 20 January 2026
Signing Flow
Right now the flow is:
- client builds tx with no funding, doesn't sign anything, sends to payserver
- payserver adds input funding + change output, and signs them, sends back to client
- client finally signs their input, and broadcasts
If we want payserver to handle the broadcast itself right after signing for funding, we would need to adapt the flow (but maybe this would introduce concurrency issues since no signing on first ping would not activate _pendingUtxos array in payserver purse, so we would need to add a new management for reserved utxos + timeout, and this would add one more round-trip so 200-800 ms of latency)
note: the only reason i'm considering this 2nd flow is bc i thought it might reduce race conditions, but since every run instance already manages its own internal queue for utxos & signing, it seems useless. Just make sure to have a different purse on each server (for ex if you have multiple payservers).
New flow would be:
- client builds, no funding, no sign, sends to payserver
- payserver adds input funding + change output, no signing, sends back to client
- client signs their input, sends to payserver again
- payserver signs their funding input + broadcast (ex with /finalize endpoint)
Pourquoi pay() doit avoir lieu avant les autres signatures
Engagement de signature : Les signatures Bitcoin hachent l’intégralité de la structure de la transaction (inputs, outputs, montants, scripts). Modifier quoi que ce soit après la signature invalide les signatures (le hash change).
Dans les transactions multi-parties :
La fonction pay() modifie la transaction (elle ajoute des inputs/outputs pour les frais) → elle doit donc intervenir avant les signatures finales sur ces nouvelles parties.
Si une partie signe en premier, puis que pay() ajoute des inputs → les signatures sont cassées (elles s’étaient engagées sur l’ancien hash).
Pourquoi ne pas utiliser SIGHASH_ANYONECANPAY ?
SIGHASH_ANYONECANPAY permettrait théoriquement au client de signer avant le pay(), mais voici pourquoi Run n’utilise pas SIGHASH_ANYONECANPAY dans son code par défaut :
Le protocole Run (avant RunCraft) utilise exclusivement SIGHASH_ALL pour garantir l’intégrité des jigs.
Sécurité et engagement :
SIGHASH_ANYONECANPAY permet à n’importe qui d’ajouter des inputs plus tard sans casser les signatures existantes. C’est très utile pour des transactions collaboratives comme les atomic swaps ou les coinjoins, mais c’est risqué pour le système de jigs de Run.
Les jigs/tokens sont stateful (propriété, méthodes, références dans OP_RETURN, etc.), donc Run veut un engagement total sur la transaction pour empêcher toute manipulation.
Si un client signait avec ANYONECANPAY, un PayServer malveillant (ou un intermédiaire) pourrait ajouter des inputs/outputs malveillants (ex. : voler des fonds).
SIGHASH_ALL garantit que le signataire accepte exactement la forme de la transaction telle qu’elle est au moment de la signature.# Markdown syntax guide