mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-11-05 05:46:44 +00:00
improve wallet import error messaging, handle p2sh-p2wpkh payjoin
This commit is contained in:
parent
0aac8bbea7
commit
bcf6f77340
6 changed files with 45 additions and 24 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit 9c9836147ab77b28fed9b6bdc8eb1e14fd1e1217
|
Subproject commit 3433c5f20524416f1d4225d7961f40b4300c4006
|
|
@ -6,10 +6,7 @@ import com.sparrowwallet.drongo.KeyDerivation;
|
||||||
import com.sparrowwallet.drongo.policy.Policy;
|
import com.sparrowwallet.drongo.policy.Policy;
|
||||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
import com.sparrowwallet.drongo.wallet.*;
|
||||||
import com.sparrowwallet.drongo.wallet.KeystoreSource;
|
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
|
||||||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -75,8 +72,10 @@ public class CoboVaultSinglesig implements KeystoreFileImport, WalletImport {
|
||||||
wallet.getKeystores().add(keystore);
|
wallet.getKeystores().add(keystore);
|
||||||
wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.SINGLE, ScriptType.P2WPKH, wallet.getKeystores(), null));
|
wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.SINGLE, ScriptType.P2WPKH, wallet.getKeystores(), null));
|
||||||
|
|
||||||
if(!wallet.isValid()) {
|
try {
|
||||||
throw new ImportException("Wallet is in an inconsistent state.");
|
wallet.checkWallet();
|
||||||
|
} catch(InvalidWalletException e) {
|
||||||
|
throw new ImportException("Imported Cobo Vault wallet was invalid: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
|
|
|
@ -8,10 +8,7 @@ import com.sparrowwallet.drongo.KeyDerivation;
|
||||||
import com.sparrowwallet.drongo.policy.Policy;
|
import com.sparrowwallet.drongo.policy.Policy;
|
||||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
import com.sparrowwallet.drongo.wallet.*;
|
||||||
import com.sparrowwallet.drongo.wallet.KeystoreSource;
|
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
|
||||||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -101,8 +98,10 @@ public class ColdcardSinglesig implements KeystoreFileImport, WalletImport {
|
||||||
wallet.getKeystores().add(keystore);
|
wallet.getKeystores().add(keystore);
|
||||||
wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.SINGLE, ScriptType.P2WPKH, wallet.getKeystores(), null));
|
wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.SINGLE, ScriptType.P2WPKH, wallet.getKeystores(), null));
|
||||||
|
|
||||||
if(!wallet.isValid()) {
|
try {
|
||||||
throw new ImportException("Wallet is in an inconsistent state.");
|
wallet.checkWallet();
|
||||||
|
} catch(InvalidWalletException e) {
|
||||||
|
throw new ImportException("Imported Coldcard wallet was invalid: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
|
|
|
@ -201,8 +201,10 @@ public class Electrum implements KeystoreFileImport, WalletImport, WalletExport
|
||||||
|
|
||||||
wallet.updateTransactions(ew.transactions);
|
wallet.updateTransactions(ew.transactions);
|
||||||
|
|
||||||
if(!wallet.isValid()) {
|
try {
|
||||||
throw new IllegalStateException("Electrum wallet is in an inconsistent state.");
|
wallet.checkWallet();
|
||||||
|
} catch(InvalidWalletException e) {
|
||||||
|
throw new IllegalStateException("Imported Electrum wallet was invalid: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.sparrowwallet.sparrow.io;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.sparrowwallet.drongo.OutputDescriptor;
|
import com.sparrowwallet.drongo.OutputDescriptor;
|
||||||
|
import com.sparrowwallet.drongo.wallet.InvalidWalletException;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||||
|
|
||||||
|
@ -57,8 +58,10 @@ public class Specter implements WalletImport, WalletExport {
|
||||||
Wallet wallet = outputDescriptor.toWallet();
|
Wallet wallet = outputDescriptor.toWallet();
|
||||||
wallet.setName(specterWallet.label);
|
wallet.setName(specterWallet.label);
|
||||||
|
|
||||||
if(!wallet.isValid()) {
|
try {
|
||||||
throw new ImportException("Specter wallet file did not contain a valid wallet");
|
wallet.checkWallet();
|
||||||
|
} catch(InvalidWalletException e) {
|
||||||
|
throw new ImportException("Imported Specter wallet was invalid: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
|
|
|
@ -36,6 +36,23 @@ public class Payjoin {
|
||||||
this.payjoinURI = payjoinURI;
|
this.payjoinURI = payjoinURI;
|
||||||
this.wallet = wallet;
|
this.wallet = wallet;
|
||||||
this.psbt = psbt.getPublicCopy();
|
this.psbt = psbt.getPublicCopy();
|
||||||
|
|
||||||
|
for(PSBTInput psbtInput : this.psbt.getPsbtInputs()) {
|
||||||
|
if(psbtInput.getUtxo() == null) {
|
||||||
|
throw new IllegalArgumentException("Original PSBT for payjoin transaction must have non_witness_utxo or witness_utxo fields for all inputs");
|
||||||
|
}
|
||||||
|
if(!psbtInput.getDerivedPublicKeys().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Original PSBT for payjoin transaction must have no derived public keys for all inputs");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!this.psbt.isFinalized()) {
|
||||||
|
throw new IllegalArgumentException("Original PSBT for payjoin transaction must be finalized");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!this.psbt.getExtendedPublicKeys().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Original PSBT for payjoin transaction must have no global xpubs");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PSBT requestPayjoinPSBT(boolean allowOutputSubstitution) throws PayjoinReceiverException {
|
public PSBT requestPayjoinPSBT(boolean allowOutputSubstitution) throws PayjoinReceiverException {
|
||||||
|
@ -57,7 +74,7 @@ public class Payjoin {
|
||||||
long maxAdditionalFeeContribution = 0;
|
long maxAdditionalFeeContribution = 0;
|
||||||
if(changeOutputIndex > -1) {
|
if(changeOutputIndex > -1) {
|
||||||
appendQuery += "&additionalfeeoutputindex=" + changeOutputIndex;
|
appendQuery += "&additionalfeeoutputindex=" + changeOutputIndex;
|
||||||
maxAdditionalFeeContribution = getAdditionalFeeContribution(psbt.getTransaction());
|
maxAdditionalFeeContribution = getAdditionalFeeContribution();
|
||||||
appendQuery += "&maxadditionalfeecontribution=" + maxAdditionalFeeContribution;
|
appendQuery += "&maxadditionalfeecontribution=" + maxAdditionalFeeContribution;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,8 +175,8 @@ public class Payjoin {
|
||||||
proposedPSBTInput.setWitnessUtxo(originalPSBTInput.getWitnessUtxo());
|
proposedPSBTInput.setWitnessUtxo(originalPSBTInput.getWitnessUtxo());
|
||||||
// We fill up information we had on the signed PSBT, so we can sign it.
|
// We fill up information we had on the signed PSBT, so we can sign it.
|
||||||
proposedPSBTInput.getDerivedPublicKeys().putAll(originalPSBTInput.getDerivedPublicKeys());
|
proposedPSBTInput.getDerivedPublicKeys().putAll(originalPSBTInput.getDerivedPublicKeys());
|
||||||
proposedPSBTInput.setRedeemScript(originalPSBTInput.getRedeemScript());
|
proposedPSBTInput.setRedeemScript(originalPSBTInput.getFinalScriptSig().getFirstNestedScript());
|
||||||
proposedPSBTInput.setWitnessScript(originalPSBTInput.getWitnessScript());
|
proposedPSBTInput.setWitnessScript(originalPSBTInput.getFinalScriptWitness().getWitnessScript());
|
||||||
proposedPSBTInput.setSigHash(originalPSBTInput.getSigHash());
|
proposedPSBTInput.setSigHash(originalPSBTInput.getSigHash());
|
||||||
} else {
|
} else {
|
||||||
// Verify the PSBT input is finalized
|
// Verify the PSBT input is finalized
|
||||||
|
@ -220,7 +237,7 @@ public class Payjoin {
|
||||||
}
|
}
|
||||||
// Make sure the actual contribution is only paying for fee incurred by additional inputs
|
// Make sure the actual contribution is only paying for fee incurred by additional inputs
|
||||||
int additionalInputsCount = proposalTx.getInputs().size() - originalTx.getInputs().size();
|
int additionalInputsCount = proposalTx.getInputs().size() - originalTx.getInputs().size();
|
||||||
if(actualContribution > getSingleInputFee(originalTx) * additionalInputsCount) {
|
if(actualContribution > getSingleInputFee() * additionalInputsCount) {
|
||||||
throw new PayjoinReceiverException("The actual contribution is not only paying for additional inputs");
|
throw new PayjoinReceiverException("The actual contribution is not only paying for additional inputs");
|
||||||
}
|
}
|
||||||
} else if(allowOutputSubstitution && originalOutput.getKey().getScript().equals(payjoinURI.getAddress().getOutputScript())) {
|
} else if(allowOutputSubstitution && originalOutput.getKey().getScript().equals(payjoinURI.getAddress().getOutputScript())) {
|
||||||
|
@ -259,11 +276,12 @@ public class Payjoin {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getAdditionalFeeContribution(Transaction transaction) {
|
private long getAdditionalFeeContribution() {
|
||||||
return getSingleInputFee(transaction);
|
return getSingleInputFee();
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getSingleInputFee(Transaction transaction) {
|
private long getSingleInputFee() {
|
||||||
|
Transaction transaction = psbt.extractTransaction();
|
||||||
double feeRate = psbt.getFee().doubleValue() / transaction.getVirtualSize();
|
double feeRate = psbt.getFee().doubleValue() / transaction.getVirtualSize();
|
||||||
int vSize = 68;
|
int vSize = 68;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue