|
|
|
|
@ -4,6 +4,7 @@ from twisted.web.client import (Agent, readBody, ResponseFailed,
|
|
|
|
|
BrowserLikePolicyForHTTPS) |
|
|
|
|
from twisted.web.iweb import IPolicyForHTTPS |
|
|
|
|
from twisted.internet.ssl import CertificateOptions |
|
|
|
|
from twisted.internet.error import ConnectionRefusedError |
|
|
|
|
from twisted.web.http_headers import Headers |
|
|
|
|
import urllib.parse as urlparse |
|
|
|
|
from urllib.parse import urlencode |
|
|
|
|
@ -444,7 +445,9 @@ def send_payjoin(manager, accept_callback=None,
|
|
|
|
|
return (False, "could not create non-payjoin payment") |
|
|
|
|
|
|
|
|
|
manager.set_payment_tx_and_psbt(payment_psbt) |
|
|
|
|
# TODO add delayed call to broadcast this after 1 minute |
|
|
|
|
|
|
|
|
|
# add delayed call to broadcast this after 1 minute |
|
|
|
|
reactor.callLater(60, fallback_nonpayjoin_broadcast, manager, b"timeout") |
|
|
|
|
|
|
|
|
|
# Now we send the request to the server, with the encoded |
|
|
|
|
# payment PSBT |
|
|
|
|
@ -484,9 +487,7 @@ def send_payjoin(manager, accept_callback=None,
|
|
|
|
|
|
|
|
|
|
destination_url = manager.server.encode("utf-8") |
|
|
|
|
url_parts = list(urlparse.urlparse(destination_url)) |
|
|
|
|
print("From destination url: ", destination_url, " got urlparts: ", url_parts) |
|
|
|
|
url_parts[4] = urlencode(params).encode("utf-8") |
|
|
|
|
print("after insertion, url_parts is: ", url_parts) |
|
|
|
|
destination_url = urlparse.urlunparse(url_parts) |
|
|
|
|
# TODO what to use as user agent? |
|
|
|
|
d = agent.request(b"POST", destination_url, |
|
|
|
|
@ -499,22 +500,35 @@ def send_payjoin(manager, accept_callback=None,
|
|
|
|
|
# by a server rejection (which is accompanied by a non-200 |
|
|
|
|
# status code returned), but by failure to communicate. |
|
|
|
|
def noResponse(failure): |
|
|
|
|
failure.trap(ResponseFailed) |
|
|
|
|
log.error(failure.value.reasons[0].getTraceback()) |
|
|
|
|
reactor.stop() |
|
|
|
|
failure.trap(ResponseFailed, ConnectionRefusedError) |
|
|
|
|
log.error(failure.value) |
|
|
|
|
fallback_nonpayjoin_broadcast(manager, b"connection refused") |
|
|
|
|
d.addErrback(noResponse) |
|
|
|
|
return (True, None) |
|
|
|
|
|
|
|
|
|
def fallback_nonpayjoin_broadcast(manager, err): |
|
|
|
|
""" Sends the non-coinjoin payment onto the network, |
|
|
|
|
assuming that the payjoin failed. The reason for failure is |
|
|
|
|
`err` and will usually be communicated by the server, and must |
|
|
|
|
be a bytestring. |
|
|
|
|
Note that the reactor is shutdown after sending the payment (one-shot |
|
|
|
|
processing). |
|
|
|
|
""" |
|
|
|
|
assert isinstance(manager, JMPayjoinManager) |
|
|
|
|
def quit(): |
|
|
|
|
for dc in reactor.getDelayedCalls(): |
|
|
|
|
dc.cancel() |
|
|
|
|
reactor.stop() |
|
|
|
|
log.warn("Payjoin did not succeed, falling back to non-payjoin payment.") |
|
|
|
|
log.warn("Error message was: " + err.decode("utf-8")) |
|
|
|
|
original_tx = manager.initial_psbt.extract_transaction() |
|
|
|
|
if not jm_single().bc_interface.pushtx(original_tx.serialize()): |
|
|
|
|
log.error("Unable to broadcast original payment. The payment is NOT made.") |
|
|
|
|
quit() |
|
|
|
|
return |
|
|
|
|
log.info("We paid without coinjoin. Transaction: ") |
|
|
|
|
log.info(btc.human_readable_transaction(original_tx)) |
|
|
|
|
reactor.stop() |
|
|
|
|
quit() |
|
|
|
|
|
|
|
|
|
def receive_payjoin_proposal_from_server(response, manager): |
|
|
|
|
assert isinstance(manager, JMPayjoinManager) |
|
|
|
|
|