broadcast tx

This commit is contained in:
Craig Raw 2020-07-30 12:18:45 +02:00
parent 75f5fd2e12
commit c9d7b03afc
6 changed files with 109 additions and 8 deletions

View file

@ -24,6 +24,7 @@ public class BalanceChart extends LineChart<Number, Number> {
} }
public void initialize(WalletTransactionsEntry walletTransactionsEntry) { public void initialize(WalletTransactionsEntry walletTransactionsEntry) {
managedProperty().bind(visibleProperty());
balanceSeries = new XYChart.Series<>(); balanceSeries = new XYChart.Series<>();
getData().add(balanceSeries); getData().add(balanceSeries);
update(walletTransactionsEntry); update(walletTransactionsEntry);
@ -33,6 +34,10 @@ public class BalanceChart extends LineChart<Number, Number> {
} }
public void update(WalletTransactionsEntry walletTransactionsEntry) { public void update(WalletTransactionsEntry walletTransactionsEntry) {
if(walletTransactionsEntry.getChildren().isEmpty()) {
setVisible(false);
}
balanceSeries.getData().clear(); balanceSeries.getData().clear();
List<Data<Number, Number>> balanceDataList = walletTransactionsEntry.getChildren().stream() List<Data<Number, Number>> balanceDataList = walletTransactionsEntry.getChildren().stream()

View file

@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.github.arteam.simplejsonrpc.client.*; import com.github.arteam.simplejsonrpc.client.*;
import com.github.arteam.simplejsonrpc.client.builder.BatchRequestBuilder; import com.github.arteam.simplejsonrpc.client.builder.BatchRequestBuilder;
import com.github.arteam.simplejsonrpc.client.exception.JsonRpcBatchException; import com.github.arteam.simplejsonrpc.client.exception.JsonRpcBatchException;
import com.github.arteam.simplejsonrpc.client.exception.JsonRpcException;
import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcMethod; import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcMethod;
import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcParam; import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcParam;
import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcService; import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcService;
@ -594,6 +595,26 @@ public class ElectrumServer {
return targetBlocksFeeRatesSats; return targetBlocksFeeRatesSats;
} }
public Sha256Hash broadcastTransaction(Transaction transaction) throws ServerException {
byte[] rawtxBytes = transaction.bitcoinSerialize();
String rawtxHex = Utils.bytesToHex(rawtxBytes);
JsonRpcClient client = new JsonRpcClient(getTransport());
try {
String strTxHash = client.createRequest().returnAs(String.class).method("blockchain.transaction.broadcast").id(1).param("raw_tx", rawtxHex).execute();
Sha256Hash receivedTxid = Sha256Hash.wrap(strTxHash);
if(!receivedTxid.equals(transaction.getTxId())) {
throw new ServerException("Received txid was different (" + receivedTxid + ")");
}
return receivedTxid;
} catch(JsonRpcException e) {
throw new ServerException(e.getErrorMessage().getMessage());
} catch(IllegalStateException e) {
throw new ServerException(e.getMessage());
}
}
public static String getScriptHash(Wallet wallet, WalletNode node) { public static String getScriptHash(Wallet wallet, WalletNode node) {
byte[] hash = Sha256Hash.hash(wallet.getOutputScript(node).getProgram()); byte[] hash = Sha256Hash.hash(wallet.getOutputScript(node).getProgram());
byte[] reversed = Utils.reverseBytes(hash); byte[] reversed = Utils.reverseBytes(hash);
@ -1139,6 +1160,24 @@ public class ElectrumServer {
} }
} }
public static class BroadcastTransactionService extends Service<Sha256Hash> {
private final Transaction transaction;
public BroadcastTransactionService(Transaction transaction) {
this.transaction = transaction;
}
@Override
protected Task<Sha256Hash> createTask() {
return new Task<>() {
protected Sha256Hash call() throws ServerException {
ElectrumServer electrumServer = new ElectrumServer();
return electrumServer.broadcastTransaction(transaction);
}
};
}
}
public enum Protocol { public enum Protocol {
TCP { TCP {
@Override @Override

View file

@ -10,6 +10,7 @@ import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.*; import com.sparrowwallet.sparrow.control.*;
import com.sparrowwallet.sparrow.event.*; import com.sparrowwallet.sparrow.event.*;
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5Brands; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5Brands;
import com.sparrowwallet.sparrow.io.ElectrumServer;
import com.sparrowwallet.sparrow.io.Storage; import com.sparrowwallet.sparrow.io.Storage;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
@ -154,6 +155,9 @@ public class HeadersController extends TransactionFormController implements Init
@FXML @FXML
private SignaturesProgressBar signaturesProgressBar; private SignaturesProgressBar signaturesProgressBar;
@FXML
private ProgressBar broadcastProgressBar;
@FXML @FXML
private HBox signButtonBox; private HBox signButtonBox;
@ -163,6 +167,12 @@ public class HeadersController extends TransactionFormController implements Init
@FXML @FXML
private HBox broadcastButtonBox; private HBox broadcastButtonBox;
@FXML
private Button viewFinalButton;
@FXML
private Button broadcastButton;
@Override @Override
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
EventManager.get().register(this); EventManager.get().register(this);
@ -302,6 +312,10 @@ public class HeadersController extends TransactionFormController implements Init
signButtonBox.managedProperty().bind(signButtonBox.visibleProperty()); signButtonBox.managedProperty().bind(signButtonBox.visibleProperty());
broadcastButtonBox.managedProperty().bind(broadcastButtonBox.visibleProperty()); broadcastButtonBox.managedProperty().bind(broadcastButtonBox.visibleProperty());
signaturesProgressBar.managedProperty().bind(signaturesProgressBar.visibleProperty());
broadcastProgressBar.managedProperty().bind(broadcastProgressBar.visibleProperty());
broadcastProgressBar.visibleProperty().bind(signaturesProgressBar.visibleProperty().not());
blockchainForm.setVisible(false); blockchainForm.setVisible(false);
signingWalletForm.setVisible(false); signingWalletForm.setVisible(false);
sigHashForm.setVisible(false); sigHashForm.setVisible(false);
@ -312,7 +326,7 @@ public class HeadersController extends TransactionFormController implements Init
if(headersForm.getBlockTransaction() != null) { if(headersForm.getBlockTransaction() != null) {
blockchainForm.setVisible(true); blockchainForm.setVisible(true);
updateBlockchainForm(headersForm.getBlockTransaction()); updateBlockchainForm(headersForm.getBlockTransaction(), AppController.getCurrentBlockHeight());
} else if(headersForm.getPsbt() != null) { } else if(headersForm.getPsbt() != null) {
PSBT psbt = headersForm.getPsbt(); PSBT psbt = headersForm.getPsbt();
@ -397,10 +411,9 @@ public class HeadersController extends TransactionFormController implements Init
feeRate.setText(String.format("%.2f", feeRateAmt) + " sats/vByte"); feeRate.setText(String.format("%.2f", feeRateAmt) + " sats/vByte");
} }
private void updateBlockchainForm(BlockTransaction blockTransaction) { private void updateBlockchainForm(BlockTransaction blockTransaction, Integer currentHeight) {
blockchainForm.setVisible(true); blockchainForm.setVisible(true);
Integer currentHeight = AppController.getCurrentBlockHeight();
if(currentHeight == null) { if(currentHeight == null) {
blockStatus.setText("Unknown"); blockStatus.setText("Unknown");
} else { } else {
@ -604,7 +617,6 @@ public class HeadersController extends TransactionFormController implements Init
} }
public void extractTransaction(ActionEvent event) { public void extractTransaction(ActionEvent event) {
Button viewFinalButton = (Button)event.getSource();
viewFinalButton.setDisable(true); viewFinalButton.setDisable(true);
Transaction finalTx = headersForm.getPsbt().extractTransaction(); Transaction finalTx = headersForm.getPsbt().extractTransaction();
@ -613,9 +625,22 @@ public class HeadersController extends TransactionFormController implements Init
} }
public void broadcastTransaction(ActionEvent event) { public void broadcastTransaction(ActionEvent event) {
broadcastButton.setDisable(true);
extractTransaction(event); extractTransaction(event);
ElectrumServer.BroadcastTransactionService broadcastTransactionService = new ElectrumServer.BroadcastTransactionService(headersForm.getTransaction());
broadcastTransactionService.setOnSucceeded(workerStateEvent -> {
//Do nothing and wait for WalletNodeHistoryChangedEvent to indicate tx is in mempool
});
broadcastTransactionService.setOnFailed(workerStateEvent -> {
broadcastProgressBar.setProgress(0);
AppController.showErrorDialog("Error broadcasting transaction", workerStateEvent.getSource().getException().getMessage());
broadcastButton.setDisable(false);
});
signaturesProgressBar.setVisible(false);
broadcastProgressBar.setProgress(-1);
broadcastTransactionService.start();
} }
@Subscribe @Subscribe
@ -635,7 +660,7 @@ public class HeadersController extends TransactionFormController implements Init
public void blockTransactionFetched(BlockTransactionFetchedEvent event) { public void blockTransactionFetched(BlockTransactionFetchedEvent event) {
if(event.getTxId().equals(headersForm.getTransaction().getTxId())) { if(event.getTxId().equals(headersForm.getTransaction().getTxId())) {
if(event.getBlockTransaction() != null) { if(event.getBlockTransaction() != null) {
updateBlockchainForm(event.getBlockTransaction()); updateBlockchainForm(event.getBlockTransaction(), AppController.getCurrentBlockHeight());
} }
Long feeAmt = calculateFee(event.getInputTransactions()); Long feeAmt = calculateFee(event.getInputTransactions());
@ -766,4 +791,29 @@ public class HeadersController extends TransactionFormController implements Init
updateTxId(); updateTxId();
} }
} }
@Subscribe
public void walletNodeHistoryChanged(WalletNodeHistoryChangedEvent event) {
if(headersForm.getSigningWallet() != null && event.getWalletNode(headersForm.getSigningWallet()) != null) {
Sha256Hash txid = headersForm.getTransaction().getTxId();
ElectrumServer.TransactionReferenceService transactionReferenceService = new ElectrumServer.TransactionReferenceService(Set.of(txid));
transactionReferenceService.setOnSucceeded(successEvent -> {
Map<Sha256Hash, BlockTransaction> transactionMap = transactionReferenceService.getValue();
BlockTransaction blockTransaction = transactionMap.get(txid);
if(blockTransaction != null) {
headersForm.setBlockTransaction(blockTransaction);
signaturesForm.setVisible(false);
updateBlockchainForm(blockTransaction, AppController.getCurrentBlockHeight());
}
});
transactionReferenceService.start();
}
}
@Subscribe
public void walletBlockHeightChanged(WalletBlockHeightChangedEvent event) {
if(headersForm.getSigningWallet() != null && event.getWallet() == headersForm.getSigningWallet() && headersForm.getBlockTransaction() != null) {
updateBlockchainForm(headersForm.getBlockTransaction(), event.getBlockHeight());
}
}
} }

View file

@ -1,6 +1,7 @@
package com.sparrowwallet.sparrow.transaction; package com.sparrowwallet.sparrow.transaction;
import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.wallet.BlockTransaction;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Node; import javafx.scene.Node;
@ -15,6 +16,10 @@ public class HeadersForm extends TransactionForm {
txdata.setTransaction(finalTransaction); txdata.setTransaction(finalTransaction);
} }
void setBlockTransaction(BlockTransaction blockTransaction) {
txdata.setBlockTransaction(blockTransaction);
}
@Override @Override
public Node getContents() throws IOException { public Node getContents() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("headers.fxml")); FXMLLoader loader = new FXMLLoader(getClass().getResource("headers.fxml"));

View file

@ -46,7 +46,7 @@
-fx-max-width: Infinity; -fx-max-width: Infinity;
} }
.signatures-progress-bar { .signatures-progress-bar, #broadcastProgressBar {
-fx-padding: 10 0 10 0; -fx-padding: 10 0 10 0;
} }

View file

@ -22,6 +22,7 @@
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.Hyperlink?> <?import javafx.scene.control.Hyperlink?>
<?import com.sparrowwallet.sparrow.control.SignaturesProgressBar?> <?import com.sparrowwallet.sparrow.control.SignaturesProgressBar?>
<?import javafx.scene.control.ProgressBar?>
<GridPane hgap="10.0" vgap="10.0" styleClass="tx-pane" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.transaction.HeadersController" stylesheets="@headers.css, @transaction.css, @../general.css"> <GridPane hgap="10.0" vgap="10.0" styleClass="tx-pane" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.transaction.HeadersController" stylesheets="@headers.css, @transaction.css, @../general.css">
<padding> <padding>
@ -174,6 +175,7 @@
<Fieldset text="Signatures" inputGrow="SOMETIMES"> <Fieldset text="Signatures" inputGrow="SOMETIMES">
<VBox> <VBox>
<SignaturesProgressBar fx:id="signaturesProgressBar" /> <SignaturesProgressBar fx:id="signaturesProgressBar" />
<ProgressBar fx:id="broadcastProgressBar" maxWidth="Infinity" prefHeight="50" />
</VBox> </VBox>
<VBox> <VBox>
<HBox fx:id="signButtonBox" styleClass="signatures-buttons" spacing="20"> <HBox fx:id="signButtonBox" styleClass="signatures-buttons" spacing="20">
@ -212,12 +214,12 @@
</Button> </Button>
</HBox> </HBox>
<HBox fx:id="broadcastButtonBox" styleClass="signatures-buttons" spacing="20"> <HBox fx:id="broadcastButtonBox" styleClass="signatures-buttons" spacing="20">
<Button HBox.hgrow="ALWAYS" text="View Final Transaction" contentDisplay="TOP" wrapText="true" textAlignment="CENTER" onAction="#extractTransaction"> <Button fx:id="viewFinalButton" HBox.hgrow="ALWAYS" text="View Final Transaction" contentDisplay="TOP" wrapText="true" textAlignment="CENTER" onAction="#extractTransaction">
<graphic> <graphic>
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="20" icon="SEARCH" /> <Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="20" icon="SEARCH" />
</graphic> </graphic>
</Button> </Button>
<Button defaultButton="true" HBox.hgrow="ALWAYS" text="Broadcast Transaction" contentDisplay="TOP" wrapText="true" textAlignment="CENTER" onAction="#broadcastTransaction"> <Button fx:id="broadcastButton" defaultButton="true" HBox.hgrow="ALWAYS" text="Broadcast Transaction" contentDisplay="TOP" wrapText="true" textAlignment="CENTER" onAction="#broadcastTransaction">
<graphic> <graphic>
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="20" icon="SATELLITE_DISH" /> <Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="20" icon="SATELLITE_DISH" />
</graphic> </graphic>