request payjoin transactions (over tor if available) in background thread

This commit is contained in:
Craig Raw 2021-05-19 12:24:46 +02:00
parent 53a447c72d
commit 1677c47500
2 changed files with 62 additions and 20 deletions

View file

@ -14,15 +14,23 @@ import com.sparrowwallet.drongo.psbt.PSBTParseException;
import com.sparrowwallet.drongo.uri.BitcoinURI;
import com.sparrowwallet.drongo.wallet.Wallet;
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.LoggerFactory;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.*;
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());
log.info("Sending PSBT to " + finalUri.toURL());
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(finalUri)
.header("Content-Type", "text/plain")
.POST(HttpRequest.BodyPublishers.ofString(base64Psbt))
.build();
Proxy proxy = AppServices.getProxy();
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();
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() + ")");
throw new PayjoinReceiverException(payjoinReceiverError.getSafeMessage());
}
PSBT proposalPsbt = PSBT.fromString(response.body());
PSBT proposalPsbt = PSBT.fromString(response.toString().trim());
checkProposal(psbt, proposalPsbt, changeOutputIndex, maxAdditionalFeeContribution, allowOutputSubstitution);
return proposalPsbt;
} catch(URISyntaxException e) {
log.error("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);
throw new PayjoinReceiverException("Payjoin receiver error", e);
} catch(PSBTParseException e) {
@ -318,4 +338,23 @@ public class Payjoin {
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);
}
};
}
}
}

View file

@ -903,13 +903,16 @@ public class HeadersController extends TransactionFormController implements Init
throw new IllegalStateException("No valid Payjoin URI");
}
try {
Payjoin payjoin = new Payjoin(payjoinURI, headersForm.getSigningWallet(), headersForm.getPsbt());
PSBT proposalPsbt = payjoin.requestPayjoinPSBT(true);
Payjoin.RequestPayjoinPSBTService requestPayjoinPSBTService = new Payjoin.RequestPayjoinPSBTService(payjoin, true);
requestPayjoinPSBTService.setOnSucceeded(successEvent -> {
PSBT proposalPsbt = requestPayjoinPSBTService.getValue();
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