From eabcf4e8f48ae18ff8d21436a2ab5e5153719944 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Tue, 26 May 2020 10:18:52 +0200 Subject: [PATCH] wallet receiving and change support --- .../com/sparrowwallet/drongo/KeyPurpose.java | 20 +++- .../sparrowwallet/drongo/wallet/Keystore.java | 4 +- .../sparrowwallet/drongo/wallet/Wallet.java | 113 ++++++++++++++++++ 3 files changed, 131 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/sparrowwallet/drongo/KeyPurpose.java b/src/main/java/com/sparrowwallet/drongo/KeyPurpose.java index 756bcbb..4563692 100644 --- a/src/main/java/com/sparrowwallet/drongo/KeyPurpose.java +++ b/src/main/java/com/sparrowwallet/drongo/KeyPurpose.java @@ -1,15 +1,27 @@ package com.sparrowwallet.drongo; +import com.sparrowwallet.drongo.crypto.ChildNumber; + public enum KeyPurpose { - RECEIVE(0), CHANGE(1); + RECEIVE(ChildNumber.ZERO), CHANGE(ChildNumber.ONE); - private final int pathIndex; + private final ChildNumber pathIndex; - KeyPurpose(int pathIndex) { + KeyPurpose(ChildNumber pathIndex) { this.pathIndex = pathIndex; } - public int getPathIndex() { + public ChildNumber getPathIndex() { return pathIndex; } + + public static KeyPurpose fromChildNumber(ChildNumber childNumber) { + for(KeyPurpose keyPurpose : values()) { + if(keyPurpose.getPathIndex().equals(childNumber)) { + return keyPurpose; + } + } + + return null; + } } diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/Keystore.java b/src/main/java/com/sparrowwallet/drongo/wallet/Keystore.java index 30593dd..d376359 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/Keystore.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/Keystore.java @@ -105,12 +105,12 @@ public class Keystore { } public DeterministicKey getKey(KeyPurpose keyPurpose, int keyIndex) { - List receivingDerivation = List.of(extendedPublicKey.getKeyChildNumber(), new ChildNumber(keyPurpose.getPathIndex()), new ChildNumber(keyIndex)); + List receivingDerivation = List.of(extendedPublicKey.getKeyChildNumber(), keyPurpose.getPathIndex(), new ChildNumber(keyIndex)); return extendedPublicKey.getKey(receivingDerivation); } public KeyDerivation getDerivation(KeyPurpose keyPurpose, int keyIndex) { - return getKeyDerivation().extend(new ChildNumber(keyPurpose.getPathIndex())).extend(new ChildNumber(keyIndex)); + return getKeyDerivation().extend(keyPurpose.getPathIndex()).extend(new ChildNumber(keyIndex)); } public boolean isValid() { diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java index b580970..80627a4 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java @@ -1,7 +1,9 @@ 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; @@ -14,12 +16,14 @@ import java.util.*; import java.util.stream.Collectors; public class Wallet { + private static final int DEFAULT_LOOKAHEAD = 20; private String name; private PolicyType policyType; private ScriptType scriptType; private Policy defaultPolicy; private List keystores = new ArrayList<>(); + private final List accountNodes = new ArrayList<>(); public Wallet() { } @@ -76,6 +80,25 @@ public class Wallet { this.keystores = keystores; } + public Node getNodes(KeyPurpose keyPurpose) { + Node purposeNode; + Optional optionalPurposeNode = accountNodes.stream().filter(node -> node.getKeyPurpose().equals(keyPurpose)).findFirst(); + if(optionalPurposeNode.isEmpty()) { + purposeNode = new Node(keyPurpose); + accountNodes.add(purposeNode); + } else { + purposeNode = optionalPurposeNode.get(); + } + + purposeNode.fillToLookAhead(getLookAhead()); + return purposeNode; + } + + public int getLookAhead() { + //TODO: Calculate using seen transactions + return DEFAULT_LOOKAHEAD; + } + public Address getAddress(KeyPurpose keyPurpose, int index) { if(policyType == PolicyType.SINGLE) { Keystore keystore = getKeystores().get(0); @@ -255,4 +278,94 @@ public class Wallet { keystore.clearPrivate(); } } + + public class Node { + private final String derivationPath; + private String label; + private Long amount; + private final List children = new ArrayList<>(); + + private final transient KeyPurpose keyPurpose; + private final transient int index; + private final transient List derivation; + + public Node(String derivationPath) { + this.derivationPath = derivationPath; + this.derivation = KeyDerivation.parsePath(derivationPath); + this.keyPurpose = KeyPurpose.fromChildNumber(derivation.get(0)); + this.index = derivation.get(derivation.size() - 1).num(); + } + + 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 int getIndex() { + return index; + } + + public KeyPurpose getKeyPurpose() { + return keyPurpose; + } + + 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 List getChildren() { + return children; + } + + public Address getAddress() { + return Wallet.this.getAddress(keyPurpose, index); + } + + public Script getOutputScript() { + return Wallet.this.getOutputScript(keyPurpose, index); + } + + public void fillToLookAhead(int lookAhead) { + for(int i = 0; i < lookAhead; i++) { + Node node = new Node(getKeyPurpose(), i); + if(!getChildren().contains(node)) { + getChildren().add(node); + } + } + } + + @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); + } + } }