mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-11-05 05:46:44 +00:00
improve handling of scan dates earlier than core pruned date
This commit is contained in:
parent
0b980f6ab5
commit
2cd64aa650
4 changed files with 62 additions and 24 deletions
|
@ -1,5 +1,6 @@
|
||||||
package com.sparrowwallet.sparrow.control;
|
package com.sparrowwallet.sparrow.control;
|
||||||
|
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import com.sparrowwallet.drongo.KeyPurpose;
|
import com.sparrowwallet.drongo.KeyPurpose;
|
||||||
import com.sparrowwallet.drongo.address.Address;
|
import com.sparrowwallet.drongo.address.Address;
|
||||||
|
@ -322,8 +323,9 @@ public class PrivateKeySweepDialog extends Dialog<Transaction> {
|
||||||
createTransaction(privateKey.getKey(), scriptType, addressUtxosService.getValue(), destAddress);
|
createTransaction(privateKey.getKey(), scriptType, addressUtxosService.getValue(), destAddress);
|
||||||
});
|
});
|
||||||
addressUtxosService.setOnFailed(failedEvent -> {
|
addressUtxosService.setOnFailed(failedEvent -> {
|
||||||
|
Throwable rootCause = Throwables.getRootCause(failedEvent.getSource().getException());
|
||||||
log.error("Error retrieving outputs for address " + fromAddress, failedEvent.getSource().getException());
|
log.error("Error retrieving outputs for address " + fromAddress, failedEvent.getSource().getException());
|
||||||
AppServices.showErrorDialog("Error retrieving outputs for address", failedEvent.getSource().getException().getMessage());
|
AppServices.showErrorDialog("Error retrieving outputs for address", rootCause.getMessage());
|
||||||
});
|
});
|
||||||
|
|
||||||
if(Config.get().getServerType() == ServerType.BITCOIN_CORE) {
|
if(Config.get().getServerType() == ServerType.BITCOIN_CORE) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.sparrow.AppServices;
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
import com.sparrowwallet.sparrow.io.Server;
|
import com.sparrowwallet.sparrow.io.Server;
|
||||||
import com.sparrowwallet.sparrow.net.Protocol;
|
import com.sparrowwallet.sparrow.net.Protocol;
|
||||||
|
import com.sparrowwallet.sparrow.net.ServerException;
|
||||||
import com.sparrowwallet.sparrow.net.cormorant.bitcoind.BitcoindClient;
|
import com.sparrowwallet.sparrow.net.cormorant.bitcoind.BitcoindClient;
|
||||||
import com.sparrowwallet.sparrow.net.cormorant.bitcoind.CormorantBitcoindException;
|
import com.sparrowwallet.sparrow.net.cormorant.bitcoind.CormorantBitcoindException;
|
||||||
import com.sparrowwallet.sparrow.net.cormorant.bitcoind.ImportFailedException;
|
import com.sparrowwallet.sparrow.net.cormorant.bitcoind.ImportFailedException;
|
||||||
|
@ -60,14 +61,18 @@ public class Cormorant {
|
||||||
bitcoindClient.importWallet(wallet);
|
bitcoindClient.importWallet(wallet);
|
||||||
return true;
|
return true;
|
||||||
} catch(ImportFailedException e) {
|
} catch(ImportFailedException e) {
|
||||||
log.debug("Failed to import wallets", e);
|
log.warn("Failed to import wallets", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkAddressImport(Address address, Date since) {
|
public void checkAddressImport(Address address, Date since) throws ServerException {
|
||||||
//Will block until address descriptor has been added
|
//Will block until address descriptor has been added
|
||||||
|
try {
|
||||||
bitcoindClient.importAddress(address, since);
|
bitcoindClient.importAddress(address, since);
|
||||||
|
} catch(ImportFailedException e) {
|
||||||
|
throw new ServerException("Failed to import address", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRunning() {
|
public boolean isRunning() {
|
||||||
|
|
|
@ -145,7 +145,15 @@ public class BitcoindClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void importWallets(Collection<Wallet> wallets) throws ImportFailedException {
|
public void importWallets(Collection<Wallet> wallets) throws ImportFailedException {
|
||||||
|
try {
|
||||||
importDescriptors(getWalletDescriptors(wallets));
|
importDescriptors(getWalletDescriptors(wallets));
|
||||||
|
} catch(ScanDateBeforePruneException e) {
|
||||||
|
List<Wallet> prePruneWallets = wallets.stream().filter(wallet -> wallet.getBirthDate() != null && wallet.getBirthDate().before(e.getPrunedDate()) && wallet.isValid()).sorted(Comparator.comparingLong(o -> o.getBirthDate().getTime())).collect(Collectors.toList());
|
||||||
|
if(!prePruneWallets.isEmpty()) {
|
||||||
|
Platform.runLater(() -> EventManager.get().post(new CormorantPruneStatusEvent("Error: Wallet birthday earlier than Bitcoin Core prune date", prePruneWallets.get(0), e.getRescanSince(), e.getPrunedDate(), legacyWalletExists)));
|
||||||
|
}
|
||||||
|
throw new ImportFailedException("Wallet birthday earlier than prune date");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void importWallet(Wallet wallet) throws ImportFailedException {
|
public void importWallet(Wallet wallet) throws ImportFailedException {
|
||||||
|
@ -153,11 +161,15 @@ public class BitcoindClient {
|
||||||
importWallets(wallet.isMasterWallet() ? wallet.getAllWallets() : wallet.getMasterWallet().getAllWallets());
|
importWallets(wallet.isMasterWallet() ? wallet.getAllWallets() : wallet.getMasterWallet().getAllWallets());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void importAddress(Address address, Date since) {
|
public void importAddress(Address address, Date since) throws ImportFailedException {
|
||||||
Map<String, ScanDate> outputDescriptors = new HashMap<>();
|
Map<String, ScanDate> outputDescriptors = new HashMap<>();
|
||||||
String addressOutputDescriptor = OutputDescriptor.toDescriptorString(address);
|
String addressOutputDescriptor = OutputDescriptor.toDescriptorString(address);
|
||||||
outputDescriptors.put(OutputDescriptor.normalize(addressOutputDescriptor), new ScanDate(since, null, false));
|
outputDescriptors.put(OutputDescriptor.normalize(addressOutputDescriptor), new ScanDate(since, null, true));
|
||||||
|
try {
|
||||||
importDescriptors(outputDescriptors);
|
importDescriptors(outputDescriptors);
|
||||||
|
} catch(ScanDateBeforePruneException e) {
|
||||||
|
throw new ImportFailedException("Address birth date earlier than prune date.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, ScanDate> getWalletDescriptors(Collection<Wallet> wallets) throws ImportFailedException {
|
private Map<String, ScanDate> getWalletDescriptors(Collection<Wallet> wallets) throws ImportFailedException {
|
||||||
|
@ -166,20 +178,6 @@ public class BitcoindClient {
|
||||||
Date earliestBirthDate = validWallets.stream().map(Wallet::getBirthDate).filter(Objects::nonNull).sorted().findFirst().orElse(null);
|
Date earliestBirthDate = validWallets.stream().map(Wallet::getBirthDate).filter(Objects::nonNull).sorted().findFirst().orElse(null);
|
||||||
Map<String, ScanDate> outputDescriptors = new LinkedHashMap<>();
|
Map<String, ScanDate> outputDescriptors = new LinkedHashMap<>();
|
||||||
for(Wallet wallet : validWallets) {
|
for(Wallet wallet : validWallets) {
|
||||||
if(pruned) {
|
|
||||||
Optional<Date> optPrunedDate = getPrunedDate();
|
|
||||||
if(optPrunedDate.isPresent() && earliestBirthDate != null) {
|
|
||||||
Date prunedDate = optPrunedDate.get();
|
|
||||||
if(earliestBirthDate.before(prunedDate)) {
|
|
||||||
if(!prunedWarningShown) {
|
|
||||||
prunedWarningShown = true;
|
|
||||||
Platform.runLater(() -> EventManager.get().post(new CormorantPruneStatusEvent("Error: Wallet birthday earlier than Bitcoin Core prune date", wallet, earliestBirthDate, prunedDate, legacyWalletExists)));
|
|
||||||
}
|
|
||||||
throw new ImportFailedException("Wallet birthday earlier than prune date");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String receiveOutputDescriptor = OutputDescriptor.getOutputDescriptor(wallet, KeyPurpose.RECEIVE).toString(false, false);
|
String receiveOutputDescriptor = OutputDescriptor.getOutputDescriptor(wallet, KeyPurpose.RECEIVE).toString(false, false);
|
||||||
addOutputDescriptor(outputDescriptors, receiveOutputDescriptor, wallet, KeyPurpose.RECEIVE, earliestBirthDate);
|
addOutputDescriptor(outputDescriptors, receiveOutputDescriptor, wallet, KeyPurpose.RECEIVE, earliestBirthDate);
|
||||||
String changeOutputDescriptor = OutputDescriptor.getOutputDescriptor(wallet, KeyPurpose.CHANGE).toString(false, false);
|
String changeOutputDescriptor = OutputDescriptor.getOutputDescriptor(wallet, KeyPurpose.CHANGE).toString(false, false);
|
||||||
|
@ -248,7 +246,7 @@ public class BitcoindClient {
|
||||||
return wallet.getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX && keyPurpose == KeyPurpose.RECEIVE ? POSTMIX_GAP_LIMIT : DEFAULT_GAP_LIMIT;
|
return wallet.getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX && keyPurpose == KeyPurpose.RECEIVE ? POSTMIX_GAP_LIMIT : DEFAULT_GAP_LIMIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void importDescriptors(Map<String, ScanDate> descriptors) {
|
private void importDescriptors(Map<String, ScanDate> descriptors) throws ScanDateBeforePruneException {
|
||||||
//Sort descriptors in alphanumeric order to avoid deadlocks, particularly with BIP47 wallets
|
//Sort descriptors in alphanumeric order to avoid deadlocks, particularly with BIP47 wallets
|
||||||
Set<String> sortedDescriptors = new TreeSet<>(descriptors.keySet());
|
Set<String> sortedDescriptors = new TreeSet<>(descriptors.keySet());
|
||||||
for(String descriptor : sortedDescriptors) {
|
for(String descriptor : sortedDescriptors) {
|
||||||
|
@ -273,7 +271,7 @@ public class BitcoindClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> addDescriptors(Map<String, ScanDate> descriptors) {
|
private Set<String> addDescriptors(Map<String, ScanDate> descriptors) throws ScanDateBeforePruneException {
|
||||||
boolean forceRescan = descriptors.values().stream().anyMatch(scanDate -> scanDate.forceRescan);
|
boolean forceRescan = descriptors.values().stream().anyMatch(scanDate -> scanDate.forceRescan);
|
||||||
if(!initialized || forceRescan) {
|
if(!initialized || forceRescan) {
|
||||||
ListDescriptorsResult listDescriptorsResult = getBitcoindService().listDescriptors(false);
|
ListDescriptorsResult listDescriptorsResult = getBitcoindService().listDescriptors(false);
|
||||||
|
@ -303,6 +301,18 @@ public class BitcoindClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(pruned) {
|
||||||
|
Optional<Date> optPrunedDate = getPrunedDate();
|
||||||
|
if(optPrunedDate.isPresent()) {
|
||||||
|
Date prunedDate = optPrunedDate.get();
|
||||||
|
Optional<ScanDate> prePruneImport = importingDescriptors.values().stream().filter(scanDate -> scanDate.rescanSince != null && scanDate.rescanSince.before(prunedDate)).findFirst();
|
||||||
|
if(prePruneImport.isPresent()) {
|
||||||
|
ScanDate scanDate = prePruneImport.get();
|
||||||
|
throw new ScanDateBeforePruneException(scanDate.rescanSince, prunedDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(!importingDescriptors.isEmpty()) {
|
if(!importingDescriptors.isEmpty()) {
|
||||||
log.debug("Importing descriptors " + importingDescriptors);
|
log.debug("Importing descriptors " + importingDescriptors);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.sparrowwallet.sparrow.net.cormorant.bitcoind;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class ScanDateBeforePruneException extends Exception {
|
||||||
|
private final Date rescanSince;
|
||||||
|
private final Date prunedDate;
|
||||||
|
|
||||||
|
public ScanDateBeforePruneException(Date rescanSince, Date prunedDate) {
|
||||||
|
this.rescanSince = rescanSince;
|
||||||
|
this.prunedDate = prunedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getRescanSince() {
|
||||||
|
return rescanSince;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getPrunedDate() {
|
||||||
|
return prunedDate;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue