wallet tx and addresses updating

This commit is contained in:
Craig Raw 2020-06-25 11:25:35 +02:00
parent d4d61b8d41
commit 150f65e7bd
11 changed files with 139 additions and 50 deletions

View file

@ -176,6 +176,7 @@ public class AppController implements Initializable {
openTransactionIdItem.disableProperty().bind(onlineProperty.not());
openWalletFile(new File("/Users/scy/.sparrow/wallets/sparta.json"));
openWalletFile(new File("/Users/scy/.sparrow/wallets/sparta-test.json"));
}
private ElectrumServer.ConnectionService createConnectionService() {

View file

@ -43,8 +43,8 @@ public class AddressTreeTable extends TreeTableView<Entry> {
labelCol.setSortable(false);
getColumns().add(labelCol);
TreeTableColumn<Entry, Long> amountCol = new TreeTableColumn<>("Value");
amountCol.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, Long> param) -> {
TreeTableColumn<Entry, Number> amountCol = new TreeTableColumn<>("Value");
amountCol.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, Number> param) -> {
return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getValue());
});
amountCol.setCellFactory(p -> new AmountCell());
@ -54,6 +54,9 @@ public class AddressTreeTable extends TreeTableView<Entry> {
setEditable(true);
setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY);
addressCol.setSortType(TreeTableColumn.SortType.ASCENDING);
getSortOrder().add(addressCol);
Integer highestUsedIndex = rootEntry.getNode().getHighestUsedIndex();
if(highestUsedIndex != null) {
scrollTo(highestUsedIndex);
@ -80,18 +83,29 @@ public class AddressTreeTable extends TreeTableView<Entry> {
RecursiveTreeItem<Entry> rootItem = new RecursiveTreeItem<>(rootEntry, Entry::getChildren);
setRoot(rootItem);
rootItem.setExpanded(true);
if(getColumns().size() > 0 && getSortOrder().isEmpty()) {
TreeTableColumn<Entry, ?> addressCol = getColumns().get(0);
getSortOrder().add(addressCol);
addressCol.setSortType(TreeTableColumn.SortType.ASCENDING);
}
}
public void updateHistory(List<WalletNode> updatedNodes) {
NodeEntry rootEntry = (NodeEntry)getRoot().getValue();
for(WalletNode updatedNode : updatedNodes) {
NodeEntry nodeEntry = new NodeEntry(rootEntry.getWallet(), updatedNode);
Optional<Entry> optEntry = rootEntry.getChildren().stream().filter(childEntry -> ((NodeEntry)childEntry).getNode().equals(updatedNode)).findFirst();
if(optEntry.isPresent()) {
int index = rootEntry.getChildren().indexOf(optEntry.get());
NodeEntry nodeEntry = new NodeEntry(rootEntry.getWallet(), updatedNode);
rootEntry.getChildren().set(index, nodeEntry);
} else {
rootEntry.getChildren().add(nodeEntry);
}
}
sort();
}
}

View file

@ -11,14 +11,14 @@ import javafx.scene.layout.Region;
import java.util.Locale;
class AmountCell extends TreeTableCell<Entry, Long> {
class AmountCell extends TreeTableCell<Entry, Number> {
public AmountCell() {
super();
getStyleClass().add("amount-cell");
}
@Override
protected void updateItem(Long amount, boolean empty) {
protected void updateItem(Number amount, boolean empty) {
super.updateItem(amount, empty);
if(empty || amount == null) {
@ -28,7 +28,7 @@ class AmountCell extends TreeTableCell<Entry, Long> {
Entry entry = getTreeTableView().getTreeItem(getIndex()).getValue();
EntryCell.applyRowStyles(this, entry);
String satsValue = String.format(Locale.ENGLISH, "%,d", amount);
String satsValue = String.format(Locale.ENGLISH, "%,d", amount.longValue());
final String btcValue = CoinLabel.getBTCFormat().format(amount.doubleValue() / Transaction.SATOSHIS_PER_BITCOIN) + " BTC";
if(entry instanceof TransactionEntry) {
@ -71,7 +71,6 @@ class AmountCell extends TreeTableCell<Entry, Long> {
}
setText(satsValue);
}
}
}

View file

@ -50,7 +50,11 @@ public class RecursiveTreeItem<T> extends TreeItem<T> {
while(change.next()){
if(change.wasAdded()){
change.getAddedSubList().forEach(t-> RecursiveTreeItem.this.getChildren().add(change.getFrom(), new RecursiveTreeItem<>(t, this.graphicsFactory, childrenFactory)));
if(change.getFrom() >= RecursiveTreeItem.this.getChildren().size()) {
change.getAddedSubList().forEach(t-> RecursiveTreeItem.this.getChildren().add(new RecursiveTreeItem<>(t, this.graphicsFactory, childrenFactory)));
} else {
change.getAddedSubList().forEach(t-> RecursiveTreeItem.this.getChildren().add(change.getFrom(), new RecursiveTreeItem<>(t, this.graphicsFactory, childrenFactory)));
}
}
if(change.wasRemoved()){

View file

@ -34,17 +34,17 @@ public class TransactionsTreeTable extends TreeTableView<Entry> {
labelCol.setSortable(true);
getColumns().add(labelCol);
TreeTableColumn<Entry, Long> amountCol = new TreeTableColumn<>("Value");
amountCol.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, Long> param) -> {
TreeTableColumn<Entry, Number> amountCol = new TreeTableColumn<>("Value");
amountCol.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, Number> param) -> {
return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getValue());
});
amountCol.setCellFactory(p -> new AmountCell());
amountCol.setSortable(true);
getColumns().add(amountCol);
TreeTableColumn<Entry, Long> balanceCol = new TreeTableColumn<>("Balance");
balanceCol.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, Long> param) -> {
return new ReadOnlyObjectWrapper<>(param.getValue().getValue() instanceof TransactionEntry ? ((TransactionEntry)param.getValue().getValue()).getBalance() : null);
TreeTableColumn<Entry, Number> balanceCol = new TreeTableColumn<>("Balance");
balanceCol.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, Number> param) -> {
return param.getValue().getValue() instanceof TransactionEntry ? ((TransactionEntry)param.getValue().getValue()).balanceProperty() : new ReadOnlyObjectWrapper<>(null);
});
balanceCol.setCellFactory(p -> new AmountCell());
balanceCol.setSortable(true);
@ -62,7 +62,7 @@ public class TransactionsTreeTable extends TreeTableView<Entry> {
setRoot(rootItem);
rootItem.setExpanded(true);
if(getColumns().size() > 0) {
if(getColumns().size() > 0 && getSortOrder().isEmpty()) {
TreeTableColumn<Entry, ?> dateCol = getColumns().get(0);
getSortOrder().add(dateCol);
dateCol.setSortType(TreeTableColumn.SortType.DESCENDING);
@ -72,5 +72,6 @@ public class TransactionsTreeTable extends TreeTableView<Entry> {
public void updateHistory(List<WalletNode> updatedNodes) {
WalletTransactionsEntry rootEntry = (WalletTransactionsEntry)getRoot().getValue();
rootEntry.updateTransactions();
sort();
}
}

View file

@ -366,7 +366,10 @@ public class Storage {
@Override
public WalletNode deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
WalletNode node = getGson(false).fromJson(json, typeOfT);
node.parseDerivation();
for(WalletNode childNode : node.getChildren()) {
childNode.parseDerivation();
if(childNode.getChildren() == null) {
childNode.setChildren(new TreeSet<>());
}

View file

@ -11,7 +11,7 @@ public abstract class Entry {
private final ObservableList<Entry> children;
public Entry(String label, List<Entry> entries) {
this.labelProperty = new SimpleStringProperty(label);
this.labelProperty = new SimpleStringProperty(this, "label", label);
this.children = FXCollections.observableList(entries);
}

View file

@ -9,7 +9,7 @@ import com.sparrowwallet.sparrow.event.WalletEntryLabelChangedEvent;
import java.util.stream.Collectors;
public class NodeEntry extends Entry {
public class NodeEntry extends Entry implements Comparable<NodeEntry> {
private final Wallet wallet;
private final WalletNode node;
@ -56,4 +56,9 @@ public class NodeEntry extends Entry {
return node.getUnspentValue();
}
@Override
public int compareTo(NodeEntry other) {
return node.compareTo(other.node);
}
}

View file

@ -10,6 +10,8 @@ import com.sparrowwallet.sparrow.event.WalletBlockHeightChangedEvent;
import com.sparrowwallet.sparrow.event.WalletEntryLabelChangedEvent;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.IntegerPropertyBase;
import javafx.beans.property.LongProperty;
import javafx.beans.property.LongPropertyBase;
import java.util.*;
import java.util.stream.Collectors;
@ -20,7 +22,6 @@ public class TransactionEntry extends Entry implements Comparable<TransactionEnt
private final Wallet wallet;
private final BlockTransaction blockTransaction;
private WalletTransactionsEntry parent;
public TransactionEntry(Wallet wallet, BlockTransaction blockTransaction, Map<BlockTransactionHashIndex, KeyPurpose> inputs, Map<BlockTransactionHashIndex, KeyPurpose> outputs) {
super(blockTransaction.getLabel(), createChildEntries(wallet, inputs, outputs));
@ -42,10 +43,6 @@ public class TransactionEntry extends Entry implements Comparable<TransactionEnt
return wallet;
}
void setParent(WalletTransactionsEntry walletTransactionsEntry) {
this.parent = walletTransactionsEntry;
}
public BlockTransaction getBlockTransaction() {
return blockTransaction;
}
@ -65,10 +62,6 @@ public class TransactionEntry extends Entry implements Comparable<TransactionEnt
return value;
}
public Long getBalance() {
return parent.getBalance(this);
}
public boolean isConfirming() {
return getConfirmations() < BLOCKS_TO_CONFIRM;
}
@ -128,13 +121,12 @@ public class TransactionEntry extends Entry implements Comparable<TransactionEnt
if (o == null || getClass() != o.getClass()) return false;
TransactionEntry that = (TransactionEntry) o;
return wallet.equals(that.wallet) &&
blockTransaction.equals(that.blockTransaction) &&
parent.equals(that.parent);
blockTransaction.equals(that.blockTransaction);
}
@Override
public int hashCode() {
return Objects.hash(wallet, blockTransaction, parent);
return Objects.hash(wallet, blockTransaction);
}
@Override
@ -175,6 +167,39 @@ public class TransactionEntry extends Entry implements Comparable<TransactionEnt
return confirmations;
}
/**
* Defines the wallet balance at the historical point of this transaction, as defined by BlockTransaction's compareTo method.
*/
private LongProperty balance;
public final void setBalance(long value) {
if(balance != null || value != 0) {
balanceProperty().set(value);
}
}
public final long getBalance() {
return balance == null ? 0L : balance.get();
}
public final LongProperty balanceProperty() {
if(balance == null) {
balance = new LongPropertyBase(0L) {
@Override
public Object getBean() {
return TransactionEntry.this;
}
@Override
public String getName() {
return "balance";
}
};
}
return balance;
}
@Subscribe
public void blockHeightChanged(WalletBlockHeightChangedEvent event) {
if(getWallet().equals(event.getWallet())) {

View file

@ -90,6 +90,8 @@ public class WalletForm {
if(!currentNode.getTransactionOutputs().equals(previousNode.getTransactionOutputs())) {
changedNodes.add(currentNode);
}
} else {
changedNodes.add(currentNode);
}
}

View file

@ -5,6 +5,8 @@ import com.sparrowwallet.drongo.wallet.BlockTransaction;
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.drongo.wallet.WalletNode;
import javafx.beans.property.LongProperty;
import javafx.beans.property.LongPropertyBase;
import java.util.*;
import java.util.stream.Collectors;
@ -15,40 +17,42 @@ public class WalletTransactionsEntry extends Entry {
public WalletTransactionsEntry(Wallet wallet) {
super(wallet.getName(), getWalletTransactions(wallet).stream().map(WalletTransaction::getTransactionEntry).collect(Collectors.toList()));
this.wallet = wallet;
getChildren().forEach(entry -> ((TransactionEntry)entry).setParent(this));
calculateBalances();
}
@Override
public Long getValue() {
return getBalance(null);
return getBalance();
}
protected Long getBalance(TransactionEntry transactionEntry) {
protected void calculateBalances() {
long balance = 0L;
for(Entry entry : getChildren()) {
balance += entry.getValue();
if(entry == transactionEntry) {
return balance;
}
//Note transaction entries must be in ascending order. This sorting is ultimately done according to BlockTransactions' comparator
getChildren().sort(Comparator.comparing(TransactionEntry.class::cast));
for(Entry entry : getChildren()) {
TransactionEntry transactionEntry = (TransactionEntry)entry;
balance += entry.getValue();
transactionEntry.setBalance(balance);
}
return balance;
setBalance(balance);
}
public void updateTransactions() {
List<Entry> current = getWalletTransactions(wallet).stream().map(WalletTransaction::getTransactionEntry).peek(entry -> entry.setParent(this)).collect(Collectors.toList());
List<Entry> current = getWalletTransactions(wallet).stream().map(WalletTransaction::getTransactionEntry).collect(Collectors.toList());
List<Entry> previous = new ArrayList<>(getChildren());
for(Entry currentEntry : current) {
int index = previous.indexOf(currentEntry);
if (index > -1) {
getChildren().set(index, currentEntry);
} else {
getChildren().add(currentEntry);
}
}
getChildren().sort(Comparator.comparing(TransactionEntry.class::cast));
List<Entry> entriesAdded = new ArrayList<>(current);
entriesAdded.removeAll(previous);
getChildren().addAll(entriesAdded);
List<Entry> entriesRemoved = new ArrayList<>(previous);
entriesRemoved.removeAll(current);
getChildren().removeAll(entriesRemoved);
calculateBalances();
}
private static Collection<WalletTransaction> getWalletTransactions(Wallet wallet) {
@ -57,9 +61,7 @@ public class WalletTransactionsEntry extends Entry {
getWalletTransactions(wallet, walletTransactionMap, wallet.getNode(KeyPurpose.RECEIVE));
getWalletTransactions(wallet, walletTransactionMap, wallet.getNode(KeyPurpose.CHANGE));
List<WalletTransaction> walletTxList = new ArrayList<>(walletTransactionMap.values());
Collections.reverse(walletTxList);
return walletTxList;
return new ArrayList<>(walletTransactionMap.values());
}
private static void getWalletTransactions(Wallet wallet, Map<BlockTransaction, WalletTransaction> walletTransactionMap, WalletNode purposeNode) {
@ -87,6 +89,39 @@ public class WalletTransactionsEntry extends Entry {
}
}
/**
* Defines the wallet balance in total.
*/
private LongProperty balance;
public final void setBalance(long value) {
if(balance != null || value != 0) {
balanceProperty().set(value);
}
}
public final long getBalance() {
return balance == null ? 0L : balance.get();
}
public final LongProperty balanceProperty() {
if(balance == null) {
balance = new LongPropertyBase(0L) {
@Override
public Object getBean() {
return WalletTransactionsEntry.this;
}
@Override
public String getName() {
return "balance";
}
};
}
return balance;
}
private static class WalletTransaction {
private final Wallet wallet;
private final BlockTransaction blockTransaction;