mirror of
https://github.com/sparrowwallet/drongo.git
synced 2024-12-27 10:36:45 +00:00
improve invalid wallet error messaging, fix public psbt
This commit is contained in:
parent
9c9836147a
commit
3433c5f205
6 changed files with 107 additions and 27 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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() {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue