mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +00:00
request payjoin transactions (over tor if available) in background thread
This commit is contained in:
parent
53a447c72d
commit
1677c47500
2 changed files with 62 additions and 20 deletions
|
@ -14,15 +14,23 @@ import com.sparrowwallet.drongo.psbt.PSBTParseException;
|
||||||
import com.sparrowwallet.drongo.uri.BitcoinURI;
|
import com.sparrowwallet.drongo.uri.BitcoinURI;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.drongo.wallet.WalletNode;
|
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||||
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
|
import com.sparrowwallet.sparrow.event.FeeRatesUpdatedEvent;
|
||||||
|
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||||
|
import com.sparrowwallet.sparrow.net.MempoolRateSize;
|
||||||
|
import com.sparrowwallet.sparrow.net.ServerException;
|
||||||
|
import com.sparrowwallet.sparrow.wallet.SendController;
|
||||||
|
import javafx.concurrent.Service;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.Proxy;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.http.HttpClient;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.net.http.HttpRequest;
|
|
||||||
import java.net.http.HttpResponse;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class Payjoin {
|
public class Payjoin {
|
||||||
|
@ -74,30 +82,42 @@ public class Payjoin {
|
||||||
URI finalUri = new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), uri.getQuery() == null ? appendQuery : uri.getQuery() + "&" + appendQuery, uri.getFragment());
|
URI finalUri = new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), uri.getQuery() == null ? appendQuery : uri.getQuery() + "&" + appendQuery, uri.getFragment());
|
||||||
log.info("Sending PSBT to " + finalUri.toURL());
|
log.info("Sending PSBT to " + finalUri.toURL());
|
||||||
|
|
||||||
HttpClient client = HttpClient.newHttpClient();
|
Proxy proxy = AppServices.getProxy();
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
|
||||||
.uri(finalUri)
|
|
||||||
.header("Content-Type", "text/plain")
|
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(base64Psbt))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
HttpURLConnection connection = proxy == null ? (HttpURLConnection)finalUri.toURL().openConnection() : (HttpURLConnection)finalUri.toURL().openConnection(proxy);
|
||||||
|
connection.setRequestMethod("POST");
|
||||||
|
connection.setRequestProperty("Content-Type", "text/plain");
|
||||||
|
connection.setDoOutput(true);
|
||||||
|
|
||||||
if(response.statusCode() != 200) {
|
try(OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream())) {
|
||||||
|
writer.write(base64Psbt);
|
||||||
|
writer.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder response = new StringBuilder();
|
||||||
|
try(BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
|
||||||
|
String responseLine;
|
||||||
|
while((responseLine = br.readLine()) != null) {
|
||||||
|
response.append(responseLine.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int statusCode = connection.getResponseCode();
|
||||||
|
|
||||||
|
if(statusCode != 200) {
|
||||||
Gson gson = new Gson();
|
Gson gson = new Gson();
|
||||||
PayjoinReceiverError payjoinReceiverError = gson.fromJson(response.body(), PayjoinReceiverError.class);
|
PayjoinReceiverError payjoinReceiverError = gson.fromJson(response.toString(), PayjoinReceiverError.class);
|
||||||
log.warn("Payjoin receiver returned an error of " + payjoinReceiverError.getErrorCode() + " (" + payjoinReceiverError.getMessage() + ")");
|
log.warn("Payjoin receiver returned an error of " + payjoinReceiverError.getErrorCode() + " (" + payjoinReceiverError.getMessage() + ")");
|
||||||
throw new PayjoinReceiverException(payjoinReceiverError.getSafeMessage());
|
throw new PayjoinReceiverException(payjoinReceiverError.getSafeMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
PSBT proposalPsbt = PSBT.fromString(response.body());
|
PSBT proposalPsbt = PSBT.fromString(response.toString().trim());
|
||||||
checkProposal(psbt, proposalPsbt, changeOutputIndex, maxAdditionalFeeContribution, allowOutputSubstitution);
|
checkProposal(psbt, proposalPsbt, changeOutputIndex, maxAdditionalFeeContribution, allowOutputSubstitution);
|
||||||
|
|
||||||
return proposalPsbt;
|
return proposalPsbt;
|
||||||
} catch(URISyntaxException e) {
|
} catch(URISyntaxException e) {
|
||||||
log.error("Invalid payjoin receiver URI", e);
|
log.error("Invalid payjoin receiver URI", e);
|
||||||
throw new PayjoinReceiverException("Invalid payjoin receiver URI", e);
|
throw new PayjoinReceiverException("Invalid payjoin receiver URI", e);
|
||||||
} catch(IOException | InterruptedException e) {
|
} catch(IOException e) {
|
||||||
log.error("Payjoin receiver error", e);
|
log.error("Payjoin receiver error", e);
|
||||||
throw new PayjoinReceiverException("Payjoin receiver error", e);
|
throw new PayjoinReceiverException("Payjoin receiver error", e);
|
||||||
} catch(PSBTParseException e) {
|
} catch(PSBTParseException e) {
|
||||||
|
@ -318,4 +338,23 @@ public class Payjoin {
|
||||||
return (message == null ? "Unknown Error" : message);
|
return (message == null ? "Unknown Error" : message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class RequestPayjoinPSBTService extends Service<PSBT> {
|
||||||
|
private final Payjoin payjoin;
|
||||||
|
private final boolean allowOutputSubstitution;
|
||||||
|
|
||||||
|
public RequestPayjoinPSBTService(Payjoin payjoin, boolean allowOutputSubstitution) {
|
||||||
|
this.payjoin = payjoin;
|
||||||
|
this.allowOutputSubstitution = allowOutputSubstitution;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Task<PSBT> createTask() {
|
||||||
|
return new Task<>() {
|
||||||
|
protected PSBT call() throws PayjoinReceiverException {
|
||||||
|
return payjoin.requestPayjoinPSBT(allowOutputSubstitution);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -903,13 +903,16 @@ public class HeadersController extends TransactionFormController implements Init
|
||||||
throw new IllegalStateException("No valid Payjoin URI");
|
throw new IllegalStateException("No valid Payjoin URI");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
Payjoin payjoin = new Payjoin(payjoinURI, headersForm.getSigningWallet(), headersForm.getPsbt());
|
||||||
Payjoin payjoin = new Payjoin(payjoinURI, headersForm.getSigningWallet(), headersForm.getPsbt());
|
Payjoin.RequestPayjoinPSBTService requestPayjoinPSBTService = new Payjoin.RequestPayjoinPSBTService(payjoin, true);
|
||||||
PSBT proposalPsbt = payjoin.requestPayjoinPSBT(true);
|
requestPayjoinPSBTService.setOnSucceeded(successEvent -> {
|
||||||
|
PSBT proposalPsbt = requestPayjoinPSBTService.getValue();
|
||||||
EventManager.get().post(new ViewPSBTEvent(payjoinButton.getScene().getWindow(), headersForm.getName() + " Payjoin", null, proposalPsbt));
|
EventManager.get().post(new ViewPSBTEvent(payjoinButton.getScene().getWindow(), headersForm.getName() + " Payjoin", null, proposalPsbt));
|
||||||
} catch(PayjoinReceiverException e) {
|
});
|
||||||
AppServices.showErrorDialog("Invalid Payjoin Transaction", e.getMessage());
|
requestPayjoinPSBTService.setOnFailed(failedEvent -> {
|
||||||
}
|
AppServices.showErrorDialog("Error Requesting Payjoin Transaction", failedEvent.getSource().getException().getMessage());
|
||||||
|
});
|
||||||
|
requestPayjoinPSBTService.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in a new issue