diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java index cd9ef03..2a0700e 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java @@ -1,9 +1,7 @@ package com.sparrowwallet.drongo.wallet; -import com.sparrowwallet.drongo.KeyDerivation; import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.address.Address; -import com.sparrowwallet.drongo.crypto.ChildNumber; import com.sparrowwallet.drongo.crypto.DeterministicKey; import com.sparrowwallet.drongo.crypto.ECKey; import com.sparrowwallet.drongo.crypto.Key; @@ -24,7 +22,7 @@ public class Wallet { private ScriptType scriptType; private Policy defaultPolicy; private List keystores = new ArrayList<>(); - private final Set purposeNodes = new TreeSet<>(); + private final Set purposeNodes = new TreeSet<>(); private final Map transactions = new HashMap<>(); public Wallet() { @@ -82,7 +80,7 @@ public class Wallet { this.keystores = keystores; } - private Set getPurposeNodes() { + private Set getPurposeNodes() { return purposeNodes; } @@ -90,11 +88,11 @@ public class Wallet { return transactions; } - public Node getNode(KeyPurpose keyPurpose) { - Node purposeNode; - Optional optionalPurposeNode = purposeNodes.stream().filter(node -> node.getKeyPurpose().equals(keyPurpose)).findFirst(); + public WalletNode getNode(KeyPurpose keyPurpose) { + WalletNode purposeNode; + Optional optionalPurposeNode = purposeNodes.stream().filter(node -> node.getKeyPurpose().equals(keyPurpose)).findFirst(); if(optionalPurposeNode.isEmpty()) { - purposeNode = new Node(keyPurpose); + purposeNode = new WalletNode(keyPurpose); purposeNodes.add(purposeNode); } else { purposeNode = optionalPurposeNode.get(); @@ -104,7 +102,7 @@ public class Wallet { return purposeNode; } - public int getLookAhead(Node node) { + public int getLookAhead(WalletNode node) { //TODO: Calculate using seen transactions int lookAhead = DEFAULT_LOOKAHEAD; Integer maxIndex = node.getHighestIndex(); @@ -115,24 +113,24 @@ public class Wallet { return lookAhead; } - public Node getFreshNode(KeyPurpose keyPurpose) { + public WalletNode getFreshNode(KeyPurpose keyPurpose) { //TODO: Calculate using seen transactions return getFreshNode(keyPurpose, null); } - public Node getFreshNode(KeyPurpose keyPurpose, Node current) { + public WalletNode getFreshNode(KeyPurpose keyPurpose, WalletNode current) { //TODO: Calculate using seen transactions int index = 0; if(current != null) { index = current.getIndex() + 1; } - Node node = getNode(keyPurpose); + WalletNode node = getNode(keyPurpose); if(index >= node.getChildren().size()) { node.fillToIndex(index); } - for(Node childNode : node.getChildren()) { + for(WalletNode childNode : node.getChildren()) { if(childNode.getIndex() == index) { return childNode; } @@ -141,7 +139,7 @@ public class Wallet { throw new IllegalStateException("Could not fill nodes to index " + index); } - public Address getAddress(Node node) { + public Address getAddress(WalletNode node) { return getAddress(node.getKeyPurpose(), node.getIndex()); } @@ -159,7 +157,7 @@ public class Wallet { } } - public Script getOutputScript(Node node) { + public Script getOutputScript(WalletNode node) { return getOutputScript(node.getKeyPurpose(), node.getIndex()); } @@ -177,7 +175,7 @@ public class Wallet { } } - public String getOutputDescriptor(Node node) { + public String getOutputDescriptor(WalletNode node) { return getOutputDescriptor(node.getKeyPurpose(), node.getIndex()); } @@ -290,22 +288,11 @@ public class Wallet { for(Keystore keystore : keystores) { copy.getKeystores().add(keystore.copy()); } - for(Wallet.Node node : purposeNodes) { - Node nodeCopy = copy.copyNode(node); - copy.getPurposeNodes().add(nodeCopy); + for(WalletNode node : purposeNodes) { + copy.getPurposeNodes().add(node.copy()); } - return copy; - } - - private Node copyNode(Node node) { - Node copy = new Node(node.derivationPath); - copy.setLabel(node.label); - copy.setAmount(node.amount); - for(Node child : node.getChildren()) { - copy.getChildren().add(copyNode(child)); - } - for(TransactionReference reference : node.getHistory()) { - copy.getHistory().add(reference.copy()); + for(String transactionId : transactions.keySet()) { + copy.getTransactions().put(transactionId, transactions.get(transactionId)); } return copy; @@ -365,134 +352,4 @@ public class Wallet { } } - public static class Node implements Comparable { - private final String derivationPath; - private String label; - private Long amount; - private Set children = new TreeSet<>(); - private Set history = new TreeSet<>(); - - private transient KeyPurpose keyPurpose; - private transient int index = -1; - private transient List derivation; - - public Node(String derivationPath) { - this.derivationPath = derivationPath; - parseDerivation(); - } - - public Node(KeyPurpose keyPurpose) { - this.derivation = List.of(keyPurpose.getPathIndex()); - this.derivationPath = KeyDerivation.writePath(derivation); - this.keyPurpose = keyPurpose; - this.index = keyPurpose.getPathIndex().num(); - } - - public Node(KeyPurpose keyPurpose, int index) { - this.derivation = List.of(keyPurpose.getPathIndex(), new ChildNumber(index)); - this.derivationPath = KeyDerivation.writePath(derivation); - this.keyPurpose = keyPurpose; - this.index = index; - } - - public String getDerivationPath() { - return derivationPath; - } - - private void parseDerivation() { - this.derivation = KeyDerivation.parsePath(derivationPath); - this.keyPurpose = KeyPurpose.fromChildNumber(derivation.get(0)); - this.index = derivation.get(derivation.size() - 1).num(); - } - - public int getIndex() { - if(index < 0) { - parseDerivation(); - } - - return index; - } - - public KeyPurpose getKeyPurpose() { - if(keyPurpose == null) { - parseDerivation(); - } - - return keyPurpose; - } - - public List getDerivation() { - if(derivation == null) { - parseDerivation(); - } - - return derivation; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - - public Long getAmount() { - return amount; - } - - public void setAmount(Long amount) { - this.amount = amount; - } - - public Set getChildren() { - return children; - } - - public void setChildren(Set children) { - this.children = children; - } - - public Set getHistory() { - return history; - } - - public void setHistory(Set history) { - this.history = history; - } - - public void fillToIndex(int index) { - for(int i = 0; i <= index; i++) { - Node node = new Node(getKeyPurpose(), i); - getChildren().add(node); - } - } - - public Integer getHighestIndex() { - Node highestNode = null; - for(Node childNode : getChildren()) { - highestNode = childNode; - } - - return highestNode == null ? null : highestNode.index; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Node node = (Node) o; - return derivationPath.equals(node.derivationPath); - } - - @Override - public int hashCode() { - return Objects.hash(derivationPath); - } - - @Override - public int compareTo(Node node) { - return getIndex() - node.getIndex(); - } - } } diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/WalletNode.java b/src/main/java/com/sparrowwallet/drongo/wallet/WalletNode.java new file mode 100644 index 0000000..baafaee --- /dev/null +++ b/src/main/java/com/sparrowwallet/drongo/wallet/WalletNode.java @@ -0,0 +1,155 @@ +package com.sparrowwallet.drongo.wallet; + +import com.sparrowwallet.drongo.KeyDerivation; +import com.sparrowwallet.drongo.KeyPurpose; +import com.sparrowwallet.drongo.crypto.ChildNumber; + +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; + +public class WalletNode implements Comparable { + private final String derivationPath; + private String label; + private Long amount; + private Set children = new TreeSet<>(); + private Set history = new TreeSet<>(); + + private transient KeyPurpose keyPurpose; + private transient int index = -1; + private transient List derivation; + + public WalletNode(String derivationPath) { + this.derivationPath = derivationPath; + parseDerivation(); + } + + public WalletNode(KeyPurpose keyPurpose) { + this.derivation = List.of(keyPurpose.getPathIndex()); + this.derivationPath = KeyDerivation.writePath(derivation); + this.keyPurpose = keyPurpose; + this.index = keyPurpose.getPathIndex().num(); + } + + public WalletNode(KeyPurpose keyPurpose, int index) { + this.derivation = List.of(keyPurpose.getPathIndex(), new ChildNumber(index)); + this.derivationPath = KeyDerivation.writePath(derivation); + this.keyPurpose = keyPurpose; + this.index = index; + } + + public String getDerivationPath() { + return derivationPath; + } + + private void parseDerivation() { + this.derivation = KeyDerivation.parsePath(derivationPath); + this.keyPurpose = KeyPurpose.fromChildNumber(derivation.get(0)); + this.index = derivation.get(derivation.size() - 1).num(); + } + + public int getIndex() { + if(index < 0) { + parseDerivation(); + } + + return index; + } + + public KeyPurpose getKeyPurpose() { + if(keyPurpose == null) { + parseDerivation(); + } + + return keyPurpose; + } + + public List getDerivation() { + if(derivation == null) { + parseDerivation(); + } + + return derivation; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public Long getAmount() { + return amount; + } + + public void setAmount(Long amount) { + this.amount = amount; + } + + public Set getChildren() { + return children; + } + + public void setChildren(Set children) { + this.children = children; + } + + public Set getHistory() { + return history; + } + + public void setHistory(Set history) { + this.history = history; + } + + public void fillToIndex(int index) { + for(int i = 0; i <= index; i++) { + WalletNode node = new WalletNode(getKeyPurpose(), i); + getChildren().add(node); + } + } + + public Integer getHighestIndex() { + WalletNode highestNode = null; + for(WalletNode childNode : getChildren()) { + highestNode = childNode; + } + + return highestNode == null ? null : highestNode.index; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WalletNode node = (WalletNode) o; + return derivationPath.equals(node.derivationPath); + } + + @Override + public int hashCode() { + return Objects.hash(derivationPath); + } + + @Override + public int compareTo(WalletNode node) { + return getIndex() - node.getIndex(); + } + + public WalletNode copy() { + WalletNode copy = new WalletNode(derivationPath); + copy.setLabel(label); + copy.setAmount(amount); + for(WalletNode child : getChildren()) { + copy.getChildren().add(child.copy()); + } + for(TransactionReference reference : getHistory()) { + copy.getHistory().add(reference.copy()); + } + + return copy; + } +}