wallet receiving and change support

This commit is contained in:
Craig Raw 2020-05-26 10:18:52 +02:00
parent c871bf829a
commit eabcf4e8f4
3 changed files with 131 additions and 6 deletions

View file

@ -1,15 +1,27 @@
package com.sparrowwallet.drongo; package com.sparrowwallet.drongo;
import com.sparrowwallet.drongo.crypto.ChildNumber;
public enum KeyPurpose { 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; this.pathIndex = pathIndex;
} }
public int getPathIndex() { public ChildNumber getPathIndex() {
return pathIndex; return pathIndex;
} }
public static KeyPurpose fromChildNumber(ChildNumber childNumber) {
for(KeyPurpose keyPurpose : values()) {
if(keyPurpose.getPathIndex().equals(childNumber)) {
return keyPurpose;
}
}
return null;
}
} }

View file

@ -105,12 +105,12 @@ public class Keystore {
} }
public DeterministicKey getKey(KeyPurpose keyPurpose, int keyIndex) { public DeterministicKey getKey(KeyPurpose keyPurpose, int keyIndex) {
List<ChildNumber> receivingDerivation = List.of(extendedPublicKey.getKeyChildNumber(), new ChildNumber(keyPurpose.getPathIndex()), new ChildNumber(keyIndex)); List<ChildNumber> receivingDerivation = List.of(extendedPublicKey.getKeyChildNumber(), keyPurpose.getPathIndex(), new ChildNumber(keyIndex));
return extendedPublicKey.getKey(receivingDerivation); return extendedPublicKey.getKey(receivingDerivation);
} }
public KeyDerivation getDerivation(KeyPurpose keyPurpose, int keyIndex) { 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() { public boolean isValid() {

View file

@ -1,7 +1,9 @@
package com.sparrowwallet.drongo.wallet; package com.sparrowwallet.drongo.wallet;
import com.sparrowwallet.drongo.KeyDerivation;
import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.address.Address; import com.sparrowwallet.drongo.address.Address;
import com.sparrowwallet.drongo.crypto.ChildNumber;
import com.sparrowwallet.drongo.crypto.DeterministicKey; import com.sparrowwallet.drongo.crypto.DeterministicKey;
import com.sparrowwallet.drongo.crypto.ECKey; import com.sparrowwallet.drongo.crypto.ECKey;
import com.sparrowwallet.drongo.crypto.Key; import com.sparrowwallet.drongo.crypto.Key;
@ -14,12 +16,14 @@ import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class Wallet { public class Wallet {
private static final int DEFAULT_LOOKAHEAD = 20;
private String name; private String name;
private PolicyType policyType; private PolicyType policyType;
private ScriptType scriptType; private ScriptType scriptType;
private Policy defaultPolicy; private Policy defaultPolicy;
private List<Keystore> keystores = new ArrayList<>(); private List<Keystore> keystores = new ArrayList<>();
private final List<Node> accountNodes = new ArrayList<>();
public Wallet() { public Wallet() {
} }
@ -76,6 +80,25 @@ public class Wallet {
this.keystores = keystores; this.keystores = keystores;
} }
public Node getNodes(KeyPurpose keyPurpose) {
Node purposeNode;
Optional<Node> 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) { public Address getAddress(KeyPurpose keyPurpose, int index) {
if(policyType == PolicyType.SINGLE) { if(policyType == PolicyType.SINGLE) {
Keystore keystore = getKeystores().get(0); Keystore keystore = getKeystores().get(0);
@ -255,4 +278,94 @@ public class Wallet {
keystore.clearPrivate(); keystore.clearPrivate();
} }
} }
public class Node {
private final String derivationPath;
private String label;
private Long amount;
private final List<Node> children = new ArrayList<>();
private final transient KeyPurpose keyPurpose;
private final transient int index;
private final transient List<ChildNumber> 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<Node> 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);
}
}
} }