improve invalid wallet error messaging, fix public psbt

This commit is contained in:
Craig Raw 2020-11-04 12:09:17 +02:00
parent 9c9836147a
commit 3433c5f205
6 changed files with 107 additions and 27 deletions

View file

@ -533,16 +533,15 @@ public class PSBT {
try { try {
PSBT publicCopy = new PSBT(serialize()); PSBT publicCopy = new PSBT(serialize());
publicCopy.extendedPublicKeys.clear(); publicCopy.extendedPublicKeys.clear();
publicCopy.globalProprietary.clear();
for(PSBTInput psbtInput : publicCopy.getPsbtInputs()) { for(PSBTInput psbtInput : publicCopy.getPsbtInputs()) {
psbtInput.clearPrivateFields(); psbtInput.getDerivedPublicKeys().clear();
psbtInput.getProprietary().clear();
} }
for(PSBTOutput psbtOutput : publicCopy.getPsbtOutputs()) { for(PSBTOutput psbtOutput : publicCopy.getPsbtOutputs()) {
psbtOutput.getDerivedPublicKeys().clear(); psbtOutput.getDerivedPublicKeys().clear();
psbtOutput.getProprietary().clear(); psbtOutput.getProprietary().clear();
} }
if(publicCopy.isFinalized()) {
publicCopy.transaction = publicCopy.extractTransaction();
}
return publicCopy; return publicCopy;
} catch(PSBTParseException e) { } catch(PSBTParseException e) {

View file

@ -538,7 +538,7 @@ public class PSBTInput {
return getWitnessUtxo() != null ? getWitnessUtxo() : (getNonWitnessUtxo() != null ? getNonWitnessUtxo().getOutputs().get(vout) : null); return getWitnessUtxo() != null ? getWitnessUtxo() : (getNonWitnessUtxo() != null ? getNonWitnessUtxo().getOutputs().get(vout) : null);
} }
public void clearPrivateFields() { public void clearNonFinalFields() {
partialSignatures.clear(); partialSignatures.clear();
sigHash = null; sigHash = null;
redeemScript = null; redeemScript = null;

View file

@ -0,0 +1,15 @@
package com.sparrowwallet.drongo.wallet;
public class InvalidKeystoreException extends Exception {
public InvalidKeystoreException() {
super();
}
public InvalidKeystoreException(String msg) {
super(msg);
}
public InvalidKeystoreException(String msg, Throwable throwable) {
super(msg, throwable);
}
}

View file

@ -0,0 +1,15 @@
package com.sparrowwallet.drongo.wallet;
public class InvalidWalletException extends Exception {
public InvalidWalletException() {
super();
}
public InvalidWalletException(String msg) {
super(msg);
}
public InvalidWalletException(String msg, Throwable throwable) {
super(msg, throwable);
}
}

View file

@ -130,25 +130,55 @@ public class Keystore {
} }
public boolean isValid() { public boolean isValid() {
if(label == null || source == null || walletModel == null || keyDerivation == null || extendedPublicKey == null) { try {
checkKeystore();
} catch(InvalidKeystoreException e) {
return false; return false;
} }
if(label.isEmpty() || label.replace(" ", "").length() > 16) { return true;
return false; }
public void checkKeystore() throws InvalidKeystoreException {
if(label == null) {
throw new InvalidKeystoreException("No label specified");
}
if(source == null) {
throw new InvalidKeystoreException("No source specified");
}
if(walletModel == null) {
throw new InvalidKeystoreException("No wallet model specified");
}
if(keyDerivation == null) {
throw new InvalidKeystoreException("No key derivation specified");
}
if(extendedPublicKey == null) {
throw new InvalidKeystoreException("No extended public key specified");
}
if(label.isEmpty()) {
throw new InvalidKeystoreException("Label too short");
}
if(label.replace(" ", "").length() > 16) {
throw new InvalidKeystoreException("Label too long");
} }
if(keyDerivation.getDerivationPath() == null || keyDerivation.getDerivationPath().isEmpty() || !KeyDerivation.isValid(keyDerivation.getDerivationPath())) { if(keyDerivation.getDerivationPath() == null || keyDerivation.getDerivationPath().isEmpty() || !KeyDerivation.isValid(keyDerivation.getDerivationPath())) {
return false; throw new InvalidKeystoreException("Invalid key derivation path of " + keyDerivation.getDerivationPath());
} }
if(keyDerivation.getMasterFingerprint() == null || keyDerivation.getMasterFingerprint().length() != 8 || !Utils.isHex(keyDerivation.getMasterFingerprint())) { if(keyDerivation.getMasterFingerprint() == null || keyDerivation.getMasterFingerprint().length() != 8 || !Utils.isHex(keyDerivation.getMasterFingerprint())) {
return false; throw new InvalidKeystoreException("Invalid master fingerprint of " + keyDerivation.getMasterFingerprint());
} }
if(source == KeystoreSource.SW_SEED) { if(source == KeystoreSource.SW_SEED) {
if(seed == null) { if(seed == null) {
return false; throw new InvalidKeystoreException("Source of " + source + " but no seed is present");
} }
if(!seed.isEncrypted()) { if(!seed.isEncrypted()) {
@ -158,15 +188,13 @@ public class Keystore {
DeterministicKey derivedKeyPublicOnly = derivedKey.dropPrivateBytes().dropParent(); DeterministicKey derivedKeyPublicOnly = derivedKey.dropPrivateBytes().dropParent();
ExtendedKey xpub = new ExtendedKey(derivedKeyPublicOnly, derivedKey.getParentFingerprint(), derivation.isEmpty() ? ChildNumber.ZERO : derivation.get(derivation.size() - 1)); ExtendedKey xpub = new ExtendedKey(derivedKeyPublicOnly, derivedKey.getParentFingerprint(), derivation.isEmpty() ? ChildNumber.ZERO : derivation.get(derivation.size() - 1));
if(!xpub.equals(getExtendedPublicKey())) { if(!xpub.equals(getExtendedPublicKey())) {
return false; throw new InvalidKeystoreException("Specified extended public key does not match public key derived from seed");
} }
} catch(MnemonicException e) { } catch(MnemonicException e) {
return false; throw new InvalidKeystoreException("Invalid mnemonic specified for seed", e);
} }
} }
} }
return true;
} }
public Keystore copy() { public Keystore copy() {

View file

@ -764,7 +764,7 @@ public class Wallet {
psbtInput.setFinalScriptSig(finalizedTxInput.getScriptSig()); psbtInput.setFinalScriptSig(finalizedTxInput.getScriptSig());
psbtInput.setFinalScriptWitness(finalizedTxInput.getWitness()); psbtInput.setFinalScriptWitness(finalizedTxInput.getWitness());
psbtInput.clearPrivateFields(); psbtInput.clearNonFinalFields();
} }
} }
} }
@ -799,43 +799,66 @@ public class Wallet {
} }
public boolean isValid() { public boolean isValid() {
if(policyType == null || scriptType == null || defaultPolicy == null || keystores.isEmpty()) { try {
checkWallet();
} catch(InvalidWalletException e) {
return false; return false;
} }
return true;
}
public void checkWallet() throws InvalidWalletException {
if(policyType == null) {
throw new InvalidWalletException("No policy type specified");
}
if(scriptType == null) {
throw new InvalidWalletException("No script type specified");
}
if(defaultPolicy == null) {
throw new InvalidWalletException("No default policy specified");
}
if(keystores.isEmpty()) {
throw new InvalidWalletException("No keystores specified");
}
if(!ScriptType.getScriptTypesForPolicyType(policyType).contains(scriptType)) { if(!ScriptType.getScriptTypesForPolicyType(policyType).contains(scriptType)) {
return false; throw new InvalidWalletException("Script type of " + scriptType + " is not valid for a policy type of " + policyType);
} }
int numSigs; int numSigs;
try { try {
numSigs = defaultPolicy.getNumSignaturesRequired(); numSigs = defaultPolicy.getNumSignaturesRequired();
} catch (Exception e) { } catch (Exception e) {
return false; throw new InvalidWalletException("Cannot determine number of required signatures to sign a transaction");
} }
if(policyType.equals(PolicyType.SINGLE) && (numSigs != 1 || keystores.size() != 1)) { if(policyType.equals(PolicyType.SINGLE) && (numSigs != 1 || keystores.size() != 1)) {
return false; throw new InvalidWalletException(policyType + " wallet needs " + numSigs + " and has " + keystores.size() + " keystores");
} }
if(policyType.equals(PolicyType.MULTI) && (numSigs < 1 || numSigs > keystores.size())) { if(policyType.equals(PolicyType.MULTI) && (numSigs < 1 || numSigs > keystores.size())) {
return false; throw new InvalidWalletException(policyType + " wallet needs " + numSigs + " and has " + keystores.size() + " keystores");
} }
if(containsDuplicateKeystoreLabels()) { if(containsDuplicateKeystoreLabels()) {
return false; throw new InvalidWalletException("Wallet keystores have duplicate labels");
} }
for(Keystore keystore : keystores) { for(Keystore keystore : keystores) {
if(!keystore.isValid()) { try {
return false; keystore.checkKeystore();
} } catch(InvalidKeystoreException e) {
if(derivationMatchesAnotherScriptType(keystore.getKeyDerivation().getDerivationPath())) { throw new InvalidWalletException("Keystore " + keystore.getLabel() + " is invalid (" + e.getMessage() + ")", e);
return false;
}
} }
return true; if(derivationMatchesAnotherScriptType(keystore.getKeyDerivation().getDerivationPath())) {
throw new InvalidWalletException("Keystore " + keystore.getLabel() + " derivation of " + keystore.getKeyDerivation().getDerivationPath() + " in " + scriptType.getName() + " wallet matches another default script type.");
}
}
} }
public boolean derivationMatchesAnotherScriptType(String derivationPath) { public boolean derivationMatchesAnotherScriptType(String derivationPath) {