mirror of
https://github.com/sparrowwallet/drongo.git
synced 2024-11-02 18:26:43 +00:00
support for standardized child wallet accounts
This commit is contained in:
parent
8fd14ce133
commit
eab42c0f05
4 changed files with 163 additions and 2 deletions
|
@ -7,6 +7,7 @@ public class Payment {
|
|||
private String label;
|
||||
private long amount;
|
||||
private boolean sendMax;
|
||||
private Type type = Type.DEFAULT;
|
||||
|
||||
public Payment(Address address, String label, long amount, boolean sendMax) {
|
||||
this.address = address;
|
||||
|
@ -46,4 +47,16 @@ public class Payment {
|
|||
public void setSendMax(boolean sendMax) {
|
||||
this.sendMax = sendMax;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
DEFAULT, WHIRLPOOL_FEE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package com.sparrowwallet.drongo.wallet;
|
||||
|
||||
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public enum StandardAccount {
|
||||
ACCOUNT_0("Account #0", new ChildNumber(0, true)),
|
||||
ACCOUNT_1("Account #1", new ChildNumber(1, true)),
|
||||
ACCOUNT_2("Account #2", new ChildNumber(2, true)),
|
||||
ACCOUNT_3("Account #3", new ChildNumber(3, true)),
|
||||
ACCOUNT_4("Account #4", new ChildNumber(4, true)),
|
||||
ACCOUNT_5("Account #5", new ChildNumber(5, true)),
|
||||
ACCOUNT_6("Account #6", new ChildNumber(6, true)),
|
||||
ACCOUNT_7("Account #7", new ChildNumber(7, true)),
|
||||
ACCOUNT_8("Account #8", new ChildNumber(8, true)),
|
||||
ACCOUNT_9("Account #9", new ChildNumber(9, true)),
|
||||
WHIRLPOOL_PREMIX("Premix", new ChildNumber(2147483645, true)),
|
||||
WHIRLPOOL_POSTMIX("Postmix", new ChildNumber(2147483646, true)),
|
||||
WHIRLPOOL_BADBANK("Badbank", new ChildNumber(2147483644, true));
|
||||
|
||||
public static final List<StandardAccount> WHIRLPOOL_ACCOUNTS = List.of(WHIRLPOOL_PREMIX, WHIRLPOOL_POSTMIX, WHIRLPOOL_BADBANK);
|
||||
|
||||
StandardAccount(String name, ChildNumber childNumber) {
|
||||
this.name = name;
|
||||
this.childNumber = childNumber;
|
||||
}
|
||||
|
||||
private final String name;
|
||||
private final ChildNumber childNumber;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ChildNumber getChildNumber() {
|
||||
return childNumber;
|
||||
}
|
||||
|
||||
public int getAccountNumber() {
|
||||
return childNumber.num();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
package com.sparrowwallet.drongo.wallet;
|
||||
|
||||
import com.sparrowwallet.drongo.BitcoinUnit;
|
||||
import com.sparrowwallet.drongo.KeyDerivation;
|
||||
import com.sparrowwallet.drongo.KeyPurpose;
|
||||
import com.sparrowwallet.drongo.Network;
|
||||
import com.sparrowwallet.drongo.address.Address;
|
||||
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||
import com.sparrowwallet.drongo.crypto.Key;
|
||||
import com.sparrowwallet.drongo.policy.Policy;
|
||||
|
@ -77,6 +79,92 @@ public class Wallet extends Persistable {
|
|||
return getMasterWallet().getName();
|
||||
}
|
||||
|
||||
public Wallet addChildWallet(StandardAccount standardAccount) {
|
||||
Wallet childWallet = this.copy();
|
||||
|
||||
if(!isMasterWallet()) {
|
||||
throw new IllegalStateException("Cannot add child wallet to existing child wallet");
|
||||
}
|
||||
|
||||
if(!childWallet.containsPrivateKeys()) {
|
||||
throw new IllegalStateException("Cannot derive child wallet xpub without private keys");
|
||||
}
|
||||
|
||||
if(childWallet.isEncrypted()) {
|
||||
throw new IllegalStateException("Cannot derive child wallet xpub from encrypted wallet");
|
||||
}
|
||||
|
||||
childWallet.setId(null);
|
||||
childWallet.setName(standardAccount.getName());
|
||||
childWallet.purposeNodes.clear();
|
||||
childWallet.transactions.clear();
|
||||
childWallet.storedBlockHeight = null;
|
||||
childWallet.gapLimit = null;
|
||||
childWallet.birthDate = null;
|
||||
|
||||
for(Keystore keystore : childWallet.getKeystores()) {
|
||||
KeyDerivation keyDerivation = keystore.getKeyDerivation();
|
||||
List<ChildNumber> childDerivation = new ArrayList<>(keyDerivation.getDerivation().subList(0, keyDerivation.getDerivation().size() - 1));
|
||||
childDerivation.add(standardAccount.getChildNumber());
|
||||
|
||||
try {
|
||||
Keystore derivedKeystore = Keystore.fromSeed(keystore.getSeed(), childDerivation);
|
||||
keystore.setKeyDerivation(derivedKeystore.getKeyDerivation());
|
||||
keystore.setExtendedPublicKey(derivedKeystore.getExtendedPublicKey());
|
||||
keystore.getSeed().setPassphrase(keystore.getSeed().getPassphrase());
|
||||
} catch(Exception e) {
|
||||
throw new IllegalStateException("Cannot derive keystore for " + standardAccount + " account", e);
|
||||
}
|
||||
}
|
||||
|
||||
childWallet.setMasterWallet(this);
|
||||
getChildWallets().add(childWallet);
|
||||
return childWallet;
|
||||
}
|
||||
|
||||
public Wallet getChildWallet(StandardAccount account) {
|
||||
for(Wallet childWallet : getChildWallets()) {
|
||||
for(Keystore keystore : childWallet.getKeystores()) {
|
||||
if(keystore.getKeyDerivation().getDerivation().get(keystore.getKeyDerivation().getDerivation().size() - 1).equals(account.getChildNumber())) {
|
||||
return childWallet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public StandardAccount getStandardAccountType() {
|
||||
int accountIndex = getAccountIndex();
|
||||
return Arrays.stream(StandardAccount.values()).filter(standardAccount -> standardAccount.getChildNumber().num() == accountIndex).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public int getAccountIndex() {
|
||||
int index = -1;
|
||||
|
||||
for(Keystore keystore : getKeystores()) {
|
||||
int keystoreAccount = getScriptType().getAccount(keystore.getKeyDerivation().getDerivationPath());
|
||||
if(keystoreAccount != -1 && (index == -1 || keystoreAccount == index)) {
|
||||
index = keystoreAccount;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
public boolean isWhirlpoolMasterWallet() {
|
||||
if(!isMasterWallet()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Set<StandardAccount> whirlpoolAccounts = new HashSet<>(Set.of(StandardAccount.WHIRLPOOL_PREMIX, StandardAccount.WHIRLPOOL_POSTMIX, StandardAccount.WHIRLPOOL_BADBANK));
|
||||
for(Wallet childWallet : getChildWallets()) {
|
||||
whirlpoolAccounts.remove(childWallet.getStandardAccountType());
|
||||
}
|
||||
|
||||
return whirlpoolAccounts.isEmpty();
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
|
|
@ -98,8 +98,20 @@ public class WalletTransaction {
|
|||
}
|
||||
|
||||
public boolean isConsolidationSend(Payment payment) {
|
||||
if(payment.getAddress() != null && getWallet() != null) {
|
||||
return getWallet().isWalletOutputScript(payment.getAddress().getOutputScript());
|
||||
return isWalletSend(getWallet(), payment);
|
||||
}
|
||||
|
||||
public boolean isPremixSend(Payment payment) {
|
||||
return isWalletSend(getWallet().getChildWallet(StandardAccount.WHIRLPOOL_PREMIX), payment);
|
||||
}
|
||||
|
||||
public boolean isBadbankSend(Payment payment) {
|
||||
return isWalletSend(getWallet().getChildWallet(StandardAccount.WHIRLPOOL_BADBANK), payment);
|
||||
}
|
||||
|
||||
public boolean isWalletSend(Wallet wallet, Payment payment) {
|
||||
if(payment.getAddress() != null && wallet != null) {
|
||||
return wallet.isWalletOutputScript(payment.getAddress().getOutputScript());
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
Loading…
Reference in a new issue