mirror of
https://github.com/sparrowwallet/drongo.git
synced 2024-12-26 01:56:44 +00:00
wallet export, settings fixes
This commit is contained in:
parent
0c9a100c6d
commit
f6414a4475
5 changed files with 86 additions and 11 deletions
|
@ -139,17 +139,25 @@ public class ExtendedKey {
|
|||
}
|
||||
|
||||
public enum Header {
|
||||
xprv("xprv", 0x0488ADE4, null),
|
||||
xprv("xprv", 0x0488ADE4, ScriptType.P2PKH),
|
||||
xpub("xpub", 0x0488B21E, ScriptType.P2PKH),
|
||||
yprv("yprv", 0x049D7878, ScriptType.P2SH_P2WPKH),
|
||||
ypub("ypub", 0x049D7CB2, ScriptType.P2SH_P2WPKH),
|
||||
zprv("zprv", 0x04b2430c, ScriptType.P2WPKH),
|
||||
zpub("zpub", 0x04B24746, ScriptType.P2WPKH),
|
||||
Yprv("Yprv", 0x0295b005, ScriptType.P2SH_P2WSH),
|
||||
Ypub("Ypub", 0x0295b43f, ScriptType.P2SH_P2WSH),
|
||||
Zprv("Zprv", 0x02aa7a99, ScriptType.P2WSH),
|
||||
Zpub("Zpub", 0x02aa7ed3, ScriptType.P2WSH),
|
||||
tpub("tpub", 0x043587cf, ScriptType.P2PKH),
|
||||
tprv("tprv", 0x04358394, null),
|
||||
tprv("tprv", 0x04358394, ScriptType.P2PKH),
|
||||
uprv("uprv", 0x044a4e28, ScriptType.P2SH_P2WPKH),
|
||||
upub("upub", 0x044a5262, ScriptType.P2SH_P2WPKH),
|
||||
vprv("vprv", 0x045f18bc, ScriptType.P2WPKH),
|
||||
vpub("vpub", 0x045f1cf6, ScriptType.P2WPKH),
|
||||
Uprv("Uprv", 0x024285b5, ScriptType.P2SH_P2WSH),
|
||||
Upub("Upub", 0x024289ef, ScriptType.P2SH_P2WSH),
|
||||
Vprv("Vprv", 0x02575048, ScriptType.P2WSH),
|
||||
Vpub("Vpub", 0x02575483, ScriptType.P2WSH);
|
||||
|
||||
private final String name;
|
||||
|
@ -184,16 +192,20 @@ public class ExtendedKey {
|
|||
throw new IllegalArgumentException("Unrecognised extended key header for extended key: " + xpub);
|
||||
}
|
||||
|
||||
public static Header fromScriptType(ScriptType scriptType) {
|
||||
for(Header extendedKeyHeader : Header.values()) {
|
||||
if(extendedKeyHeader.defaultScriptType != null && extendedKeyHeader.defaultScriptType.equals(scriptType)) {
|
||||
return extendedKeyHeader;
|
||||
public static Header fromScriptType(ScriptType scriptType, boolean privateKey) {
|
||||
for(Header header : Header.values()) {
|
||||
if(header.defaultScriptType != null && header.defaultScriptType.equals(scriptType) && header.isPrivate() == privateKey) {
|
||||
return header;
|
||||
}
|
||||
}
|
||||
|
||||
return Header.xpub;
|
||||
}
|
||||
|
||||
private boolean isPrivate() {
|
||||
return name.endsWith("prv");
|
||||
}
|
||||
|
||||
public static boolean isValidHeader(int header) {
|
||||
for(Header extendedKeyHeader : Header.values()) {
|
||||
if(header == extendedKeyHeader.header) {
|
||||
|
|
|
@ -98,6 +98,15 @@ public class DeterministicSeed implements EncryptableItem {
|
|||
this.creationTimeSeconds = creationTimeSeconds;
|
||||
}
|
||||
|
||||
public boolean needPassphrase() {
|
||||
if(isEncrypted()) {
|
||||
throw new IllegalArgumentException("Cannot determine if passphrase is required in encrypted state");
|
||||
}
|
||||
|
||||
byte[] mnemonicOnlySeed = Bip39MnemonicCode.toSeed(mnemonicCode, "");
|
||||
return Arrays.equals(mnemonicOnlySeed, seed);
|
||||
}
|
||||
|
||||
private static byte[] getEntropy(SecureRandom random, int bits) {
|
||||
if(bits > MAX_SEED_ENTROPY_BITS) {
|
||||
throw new IllegalArgumentException("Requested entropy size too large");
|
||||
|
@ -188,6 +197,14 @@ public class DeterministicSeed implements EncryptableItem {
|
|||
return new DeterministicSeed(mnemonic, seed, passphrase, creationTimeSeconds);
|
||||
}
|
||||
|
||||
public DeterministicSeed setPassphrase(String passphrase) {
|
||||
if(isEncrypted()) {
|
||||
throw new UnsupportedOperationException("Cannot set passphrase on encrypted seed");
|
||||
}
|
||||
|
||||
return new DeterministicSeed(mnemonicCode, seed, passphrase, creationTimeSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
|
@ -90,10 +90,20 @@ public class Keystore {
|
|||
return HDKeyDerivation.createMasterPrivateKey(seed.getSeedBytes());
|
||||
}
|
||||
|
||||
public ExtendedKey getExtendedPrivateKey() {
|
||||
public ExtendedKey getExtendedMasterPrivateKey() {
|
||||
return new ExtendedKey(getMasterPrivateKey(), new byte[4], ChildNumber.ZERO);
|
||||
}
|
||||
|
||||
public ExtendedKey getExtendedMasterPublicKey() {
|
||||
return new ExtendedKey(getMasterPrivateKey().dropPrivateBytes(), new byte[4], ChildNumber.ZERO);
|
||||
}
|
||||
|
||||
public ExtendedKey getExtendedPrivateKey() {
|
||||
List<ChildNumber> derivation = getKeyDerivation().getDerivation();
|
||||
DeterministicKey derivedKey = getExtendedMasterPrivateKey().getKey(derivation);
|
||||
return new ExtendedKey(derivedKey, derivedKey.getParentFingerprint(), derivation.get(derivation.size() - 1));
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
if(label == null || source == null || walletModel == null || keyDerivation == null || extendedPublicKey == null) {
|
||||
return false;
|
||||
|
@ -107,7 +117,19 @@ public class Keystore {
|
|||
return false;
|
||||
}
|
||||
|
||||
//TODO: If source is SW_SEED, check seed field is filled
|
||||
if(source == KeystoreSource.SW_SEED) {
|
||||
if(seed == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<ChildNumber> derivation = getKeyDerivation().getDerivation();
|
||||
DeterministicKey derivedKey = getExtendedMasterPrivateKey().getKey(derivation);
|
||||
DeterministicKey derivedKeyPublicOnly = derivedKey.dropPrivateBytes().dropParent();
|
||||
ExtendedKey xpub = new ExtendedKey(derivedKeyPublicOnly, derivedKey.getParentFingerprint(), derivation.get(derivation.size() - 1));
|
||||
if(!xpub.equals(getExtendedPublicKey())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -131,7 +153,7 @@ public class Keystore {
|
|||
public static Keystore fromSeed(DeterministicSeed seed, List<ChildNumber> derivation) {
|
||||
Keystore keystore = new Keystore();
|
||||
keystore.setSeed(seed);
|
||||
ExtendedKey xprv = keystore.getExtendedPrivateKey();
|
||||
ExtendedKey xprv = keystore.getExtendedMasterPrivateKey();
|
||||
String masterFingerprint = Utils.bytesToHex(xprv.getKey().getFingerprint());
|
||||
DeterministicKey derivedKey = xprv.getKey(derivation);
|
||||
DeterministicKey derivedKeyPublicOnly = derivedKey.dropPrivateBytes().dropParent();
|
||||
|
@ -146,6 +168,18 @@ public class Keystore {
|
|||
return keystore;
|
||||
}
|
||||
|
||||
public void setPassphrase(String passphrase) {
|
||||
if(seed != null) {
|
||||
seed = seed.setPassphrase(passphrase);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Cannot set passphrase on a keystore without a seed");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasSeed() {
|
||||
return seed != null;
|
||||
}
|
||||
|
||||
public boolean isEncrypted() {
|
||||
return seed != null && seed.isEncrypted();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import com.sparrowwallet.drongo.protocol.ScriptType;
|
|||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -102,11 +103,22 @@ public class Wallet {
|
|||
if(!keystore.isValid()) {
|
||||
return false;
|
||||
}
|
||||
if(derivationMatchesAnotherScriptType(keystore.getKeyDerivation().getDerivationPath())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean derivationMatchesAnotherScriptType(String derivationPath) {
|
||||
if(scriptType != null && scriptType.getAccount(derivationPath) > -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Arrays.stream(ScriptType.values()).anyMatch(scriptType -> !scriptType.equals(this.scriptType) && scriptType.getAccount(derivationPath) > -1);
|
||||
}
|
||||
|
||||
public Wallet copy() {
|
||||
Wallet copy = new Wallet(name);
|
||||
copy.setPolicyType(policyType);
|
||||
|
|
|
@ -14,7 +14,7 @@ public class KeystoreTest {
|
|||
DeterministicSeed seed = new DeterministicSeed(Utils.hexToBytes("727ecfcf0bce9d8ec0ef066f7aeb845c271bdd4ee06a37398cebd40dc810140bb620b6c10a8ad671afdceaf37aa55d92d6478f747e8b92430dd938ab5be961dd"), Collections.emptyList(), 0);
|
||||
keystore.setSeed(seed);
|
||||
|
||||
Assert.assertEquals("xprv9s21ZrQH143K3rN5vhm4bKDKsk1PmUK1mzxSMwkVSp2GbomwGmjLaGqrs8Nn9r14jCsfCNWfTR6pAtCsJutUH6QSHX65CePNW3YVyGxqvJa", keystore.getExtendedPrivateKey().toString());
|
||||
Assert.assertEquals("xprv9s21ZrQH143K3rN5vhm4bKDKsk1PmUK1mzxSMwkVSp2GbomwGmjLaGqrs8Nn9r14jCsfCNWfTR6pAtCsJutUH6QSHX65CePNW3YVyGxqvJa", keystore.getExtendedMasterPrivateKey().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -23,7 +23,7 @@ public class KeystoreTest {
|
|||
DeterministicSeed seed = new DeterministicSeed(Utils.hexToBytes("4d8c47d0d6241169d0b17994219211c4a980f7146bb70dbc897416790e9de23a6265c708b88176e24f6eb7378a7c55cd4bdc067cafe574eaf3480f9a41c3c43c"), Collections.emptyList(), 0);
|
||||
keystore.setSeed(seed);
|
||||
|
||||
Assert.assertEquals("xprv9s21ZrQH143K4AkrxAyivDeTCWhZV6fdLfBRR8QerWe9hHiRqMjBMj9MFNef7oFufgcDcW54LhguPNm6MVLEMWPX5qxKhmNjCzi9Zy6yhkc", keystore.getExtendedPrivateKey().toString());
|
||||
Assert.assertEquals("xprv9s21ZrQH143K4AkrxAyivDeTCWhZV6fdLfBRR8QerWe9hHiRqMjBMj9MFNef7oFufgcDcW54LhguPNm6MVLEMWPX5qxKhmNjCzi9Zy6yhkc", keystore.getExtendedMasterPrivateKey().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in a new issue