mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2025-01-24 17:31:10 +00:00
retrieve, store and use device registrations to avoid unncessary reregistration on ledger multisig wallets
This commit is contained in:
parent
95200c7143
commit
ee2f387cd5
10 changed files with 130 additions and 35 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
|||
Subproject commit 0df1f79e5c7e9fc1daa212c875c9da5dbcc0ee56
|
||||
Subproject commit f67a2caf5379a6931040f3a9b0c9203e9bfc3f44
|
|
@ -16,11 +16,8 @@ import com.sparrowwallet.drongo.wallet.*;
|
|||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.*;
|
||||
import com.sparrowwallet.sparrow.io.CardApi;
|
||||
import com.sparrowwallet.sparrow.io.Device;
|
||||
import com.sparrowwallet.sparrow.io.Hwi;
|
||||
import com.sparrowwallet.sparrow.io.*;
|
||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||
import com.sparrowwallet.sparrow.io.CardAuthorizationException;
|
||||
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
|
@ -778,10 +775,12 @@ public class DevicePane extends TitledDescriptionPane {
|
|||
signButton.setDisable(false);
|
||||
}
|
||||
} else {
|
||||
Hwi.SignPSBTService signPSBTService = new Hwi.SignPSBTService(device, passphrase.get(), psbt, OutputDescriptor.getOutputDescriptor(wallet), wallet.getFullName());
|
||||
Hwi.SignPSBTService signPSBTService = new Hwi.SignPSBTService(device, passphrase.get(), psbt,
|
||||
OutputDescriptor.getOutputDescriptor(wallet), wallet.getFullName(), getDeviceRegistration());
|
||||
signPSBTService.setOnSucceeded(workerStateEvent -> {
|
||||
PSBT signedPsbt = signPSBTService.getValue();
|
||||
EventManager.get().post(new PSBTSignedEvent(psbt, signedPsbt));
|
||||
updateDeviceRegistrations(signPSBTService.getNewDeviceRegistrations());
|
||||
});
|
||||
signPSBTService.setOnFailed(workerStateEvent -> {
|
||||
setError("Signing Error", signPSBTService.getException().getMessage());
|
||||
|
@ -821,10 +820,11 @@ public class DevicePane extends TitledDescriptionPane {
|
|||
|
||||
private void displayAddress() {
|
||||
Hwi.DisplayAddressService displayAddressService = new Hwi.DisplayAddressService(device, passphrase.get(), wallet.getScriptType(), outputDescriptor,
|
||||
OutputDescriptor.getOutputDescriptor(wallet), wallet.getFullName());
|
||||
OutputDescriptor.getOutputDescriptor(wallet), wallet.getFullName(), getDeviceRegistration());
|
||||
displayAddressService.setOnSucceeded(successEvent -> {
|
||||
String address = displayAddressService.getValue();
|
||||
EventManager.get().post(new AddressDisplayedEvent(address));
|
||||
updateDeviceRegistrations(displayAddressService.getNewDeviceRegistrations());
|
||||
});
|
||||
displayAddressService.setOnFailed(failedEvent -> {
|
||||
setError("Could not display address", displayAddressService.getException().getMessage());
|
||||
|
@ -834,6 +834,26 @@ public class DevicePane extends TitledDescriptionPane {
|
|||
displayAddressService.start();
|
||||
}
|
||||
|
||||
private byte[] getDeviceRegistration() {
|
||||
Optional<Keystore> optKeystore = wallet.getKeystores().stream()
|
||||
.filter(keystore -> keystore.getKeyDerivation().getMasterFingerprint().equals(device.getFingerprint()) && keystore.getDeviceRegistration() != null).findFirst();
|
||||
return optKeystore.map(Keystore::getDeviceRegistration).orElse(null);
|
||||
}
|
||||
|
||||
private void updateDeviceRegistrations(Set<byte[]> newDeviceRegistrations) {
|
||||
if(!newDeviceRegistrations.isEmpty()) {
|
||||
List<Keystore> registrationKeystores = getDeviceRegistrationKeystores();
|
||||
if(!registrationKeystores.isEmpty()) {
|
||||
registrationKeystores.forEach(keystore -> keystore.setDeviceRegistration(newDeviceRegistrations.iterator().next()));
|
||||
EventManager.get().post(new KeystoreDeviceRegistrationsChangedEvent(wallet, registrationKeystores));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Keystore> getDeviceRegistrationKeystores() {
|
||||
return wallet.getKeystores().stream().filter(keystore -> keystore.getKeyDerivation().getMasterFingerprint().equals(device.getFingerprint())).toList();
|
||||
}
|
||||
|
||||
private void signMessage() {
|
||||
if(device.isCard()) {
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package com.sparrowwallet.sparrow.event;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KeystoreDeviceRegistrationsChangedEvent extends WalletChangedEvent {
|
||||
private final List<Keystore> changedKeystores;
|
||||
|
||||
public KeystoreDeviceRegistrationsChangedEvent(Wallet wallet, List<Keystore> changedKeystores) {
|
||||
super(wallet);
|
||||
this.changedKeystores = changedKeystores;
|
||||
}
|
||||
|
||||
public List<Keystore> getChangedKeystores() {
|
||||
return changedKeystores;
|
||||
}
|
||||
}
|
|
@ -36,6 +36,8 @@ public class Hwi {
|
|||
|
||||
private static boolean isPromptActive = false;
|
||||
|
||||
private final Set<byte[]> newDeviceRegistrations = new HashSet<>();
|
||||
|
||||
static {
|
||||
//deleteHwiDir();
|
||||
}
|
||||
|
@ -161,7 +163,10 @@ public class Hwi {
|
|||
|
||||
isPromptActive = true;
|
||||
Lark lark = getLark(passphrase, walletDescriptor, walletName, walletRegistration);
|
||||
return lark.displayAddress(device.getType(), device.getPath(), addressDescriptor);
|
||||
String address = lark.displayAddress(device.getType(), device.getPath(), addressDescriptor);
|
||||
newDeviceRegistrations.addAll(lark.getWalletRegistrations().values());
|
||||
newDeviceRegistrations.remove(walletRegistration);
|
||||
return address;
|
||||
} catch(DeviceException e) {
|
||||
throw new DisplayAddressException(e.getMessage(), e);
|
||||
} catch(RuntimeException e) {
|
||||
|
@ -192,7 +197,10 @@ public class Hwi {
|
|||
try {
|
||||
isPromptActive = true;
|
||||
Lark lark = getLark(passphrase, walletDescriptor, walletName, walletRegistration);
|
||||
return lark.signTransaction(device.getType(), device.getPath(), psbt);
|
||||
PSBT signed = lark.signTransaction(device.getType(), device.getPath(), psbt);
|
||||
newDeviceRegistrations.addAll(lark.getWalletRegistrations().values());
|
||||
newDeviceRegistrations.remove(walletRegistration);
|
||||
return signed;
|
||||
} catch(DeviceException e) {
|
||||
throw new SignTransactionException(e.getMessage(), e);
|
||||
} catch(RuntimeException e) {
|
||||
|
@ -346,16 +354,7 @@ public class Hwi {
|
|||
private final OutputDescriptor walletDescriptor;
|
||||
private final String walletName;
|
||||
private final byte[] walletRegistration;
|
||||
|
||||
public DisplayAddressService(Device device, String passphrase, ScriptType scriptType, OutputDescriptor addressDescriptor, OutputDescriptor walletDescriptor, String walletName) {
|
||||
this.device = device;
|
||||
this.passphrase = passphrase;
|
||||
this.scriptType = scriptType;
|
||||
this.addressDescriptor = addressDescriptor;
|
||||
this.walletDescriptor = walletDescriptor;
|
||||
this.walletName = walletName;
|
||||
this.walletRegistration = null;
|
||||
}
|
||||
private final Set<byte[]> newDeviceRegistrations = new HashSet<>();
|
||||
|
||||
public DisplayAddressService(Device device, String passphrase, ScriptType scriptType, OutputDescriptor addressDescriptor, OutputDescriptor walletDescriptor, String walletName, byte[] walletRegistration) {
|
||||
this.device = device;
|
||||
|
@ -367,12 +366,18 @@ public class Hwi {
|
|||
this.walletRegistration = walletRegistration;
|
||||
}
|
||||
|
||||
public Set<byte[]> getNewDeviceRegistrations() {
|
||||
return newDeviceRegistrations;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Task<String> createTask() {
|
||||
return new Task<>() {
|
||||
protected String call() throws DisplayAddressException {
|
||||
Hwi hwi = new Hwi();
|
||||
return hwi.displayAddress(device, passphrase, scriptType, addressDescriptor, walletDescriptor, walletName, walletRegistration);
|
||||
String address = hwi.displayAddress(device, passphrase, scriptType, addressDescriptor, walletDescriptor, walletName, walletRegistration);
|
||||
newDeviceRegistrations.addAll(hwi.newDeviceRegistrations);
|
||||
return address;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -453,15 +458,7 @@ public class Hwi {
|
|||
private final OutputDescriptor walletDescriptor;
|
||||
private final String walletName;
|
||||
private final byte[] walletRegistration;
|
||||
|
||||
public SignPSBTService(Device device, String passphrase, PSBT psbt, OutputDescriptor walletDescriptor, String walletName) {
|
||||
this.device = device;
|
||||
this.passphrase = passphrase;
|
||||
this.psbt = psbt;
|
||||
this.walletDescriptor = walletDescriptor;
|
||||
this.walletName = walletName;
|
||||
this.walletRegistration = null;
|
||||
}
|
||||
private final Set<byte[]> newDeviceRegistrations = new HashSet<>();
|
||||
|
||||
public SignPSBTService(Device device, String passphrase, PSBT psbt, OutputDescriptor walletDescriptor, String walletName, byte[] walletRegistration) {
|
||||
this.device = device;
|
||||
|
@ -472,12 +469,18 @@ public class Hwi {
|
|||
this.walletRegistration = walletRegistration;
|
||||
}
|
||||
|
||||
public Set<byte[]> getNewDeviceRegistrations() {
|
||||
return newDeviceRegistrations;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Task<PSBT> createTask() {
|
||||
return new Task<>() {
|
||||
protected PSBT call() throws SignTransactionException {
|
||||
Hwi hwi = new Hwi();
|
||||
return hwi.signPSBT(device, passphrase, psbt, walletDescriptor, walletName, walletRegistration);
|
||||
PSBT signed = hwi.signPSBT(device, passphrase, psbt, walletDescriptor, walletName, walletRegistration);
|
||||
newDeviceRegistrations.addAll(hwi.newDeviceRegistrations);
|
||||
return signed;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -355,6 +355,13 @@ public class DbPersistence implements Persistence {
|
|||
}
|
||||
}
|
||||
|
||||
if(!dirtyPersistables.registrationKeystores.isEmpty()) {
|
||||
KeystoreDao keystoreDao = handle.attach(KeystoreDao.class);
|
||||
for(Keystore keystore : dirtyPersistables.registrationKeystores) {
|
||||
keystoreDao.updateDeviceRegistration(keystore.getDeviceRegistration(), keystore.getId());
|
||||
}
|
||||
}
|
||||
|
||||
dirtyPersistablesMap.remove(wallet);
|
||||
} finally {
|
||||
walletDao.setSchema(DEFAULT_SCHEMA);
|
||||
|
@ -792,6 +799,13 @@ public class DbPersistence implements Persistence {
|
|||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void keystoreDeviceRegistrationsChanged(KeystoreDeviceRegistrationsChangedEvent event) {
|
||||
if(persistsFor(event.getWallet())) {
|
||||
updateExecutor.execute(() -> dirtyPersistablesMap.computeIfAbsent(event.getWallet(), key -> new DirtyPersistables()).registrationKeystores.addAll(event.getChangedKeystores()));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletWatchLastChanged(WalletWatchLastChangedEvent event) {
|
||||
if(persistsFor(event.getWallet())) {
|
||||
|
@ -815,6 +829,7 @@ public class DbPersistence implements Persistence {
|
|||
public final Map<Sha256Hash, UtxoMixData> removedUtxoMixes = new HashMap<>();
|
||||
public final List<Keystore> labelKeystores = new ArrayList<>();
|
||||
public final List<Keystore> encryptionKeystores = new ArrayList<>();
|
||||
public final List<Keystore> registrationKeystores = new ArrayList<>();
|
||||
|
||||
public String toString() {
|
||||
return "Dirty Persistables" +
|
||||
|
@ -834,7 +849,8 @@ public class DbPersistence implements Persistence {
|
|||
"\nUTXO mixes changed:" + changedUtxoMixes +
|
||||
"\nUTXO mixes removed:" + removedUtxoMixes +
|
||||
"\nKeystore labels:" + labelKeystores.stream().map(Keystore::getLabel).collect(Collectors.toList()) +
|
||||
"\nKeystore encryptions:" + encryptionKeystores.stream().map(Keystore::getLabel).collect(Collectors.toList());
|
||||
"\nKeystore encryptions:" + encryptionKeystores.stream().map(Keystore::getLabel).collect(Collectors.toList()) +
|
||||
"\nKeystore registrations:" + registrationKeystores.stream().map(Keystore::getDeviceRegistration).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,16 +13,16 @@ import org.jdbi.v3.sqlobject.statement.SqlUpdate;
|
|||
import java.util.List;
|
||||
|
||||
public interface KeystoreDao {
|
||||
@SqlQuery("select keystore.id, keystore.label, keystore.source, keystore.walletModel, keystore.masterFingerprint, keystore.derivationPath, keystore.extendedPublicKey, keystore.externalPaymentCode, " +
|
||||
@SqlQuery("select keystore.id, keystore.label, keystore.source, keystore.walletModel, keystore.masterFingerprint, keystore.derivationPath, keystore.extendedPublicKey, keystore.externalPaymentCode, keystore.deviceRegistration, " +
|
||||
"masterPrivateExtendedKey.id, masterPrivateExtendedKey.privateKey, masterPrivateExtendedKey.chainCode, masterPrivateExtendedKey.initialisationVector, masterPrivateExtendedKey.encryptedBytes, masterPrivateExtendedKey.keySalt, masterPrivateExtendedKey.deriver, masterPrivateExtendedKey.crypter, " +
|
||||
"seed.id, seed.type, seed.mnemonicString, seed.initialisationVector, seed.encryptedBytes, seed.keySalt, seed.deriver, seed.crypter, seed.needsPassphrase, seed.creationTimeSeconds " +
|
||||
"from keystore left join masterPrivateExtendedKey on keystore.masterPrivateExtendedKey = masterPrivateExtendedKey.id left join seed on keystore.seed = seed.id where keystore.wallet = ? order by keystore.index asc")
|
||||
@RegisterRowMapper(KeystoreMapper.class)
|
||||
List<Keystore> getForWalletId(Long id);
|
||||
|
||||
@SqlUpdate("insert into keystore (label, source, walletModel, masterFingerprint, derivationPath, extendedPublicKey, externalPaymentCode, masterPrivateExtendedKey, seed, wallet, index) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
||||
@SqlUpdate("insert into keystore (label, source, walletModel, masterFingerprint, derivationPath, extendedPublicKey, externalPaymentCode, deviceRegistration, masterPrivateExtendedKey, seed, wallet, index) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
||||
@GetGeneratedKeys("id")
|
||||
long insert(String label, int source, int walletModel, String masterFingerprint, String derivationPath, String extendedPublicKey, String externalPaymentCode, Long masterPrivateExtendedKey, Long seed, long wallet, int index);
|
||||
long insert(String label, int source, int walletModel, String masterFingerprint, String derivationPath, String extendedPublicKey, String externalPaymentCode, byte[] deviceRegistration, Long masterPrivateExtendedKey, Long seed, long wallet, int index);
|
||||
|
||||
@SqlUpdate("insert into masterPrivateExtendedKey (privateKey, chainCode, initialisationVector, encryptedBytes, keySalt, deriver, crypter, creationTimeSeconds) values (?, ?, ?, ?, ?, ?, ?, ?)")
|
||||
@GetGeneratedKeys("id")
|
||||
|
@ -41,6 +41,9 @@ public interface KeystoreDao {
|
|||
@SqlUpdate("update keystore set label = ? where id = ?")
|
||||
void updateLabel(String label, long id);
|
||||
|
||||
@SqlUpdate("update keystore set deviceRegistration = ? where id = ?")
|
||||
void updateDeviceRegistration(byte[] deviceRegistration, long id);
|
||||
|
||||
default void addKeystores(Wallet wallet) {
|
||||
for(int i = 0; i < wallet.getKeystores().size(); i++) {
|
||||
Keystore keystore = wallet.getKeystores().get(i);
|
||||
|
@ -73,6 +76,7 @@ public interface KeystoreDao {
|
|||
keystore.getKeyDerivation().getDerivationPath(),
|
||||
keystore.hasMasterPrivateKey() || wallet.isBip47() ? null : keystore.getExtendedPublicKey().toString(),
|
||||
keystore.getExternalPaymentCode() == null ? null : keystore.getExternalPaymentCode().toString(),
|
||||
keystore.getDeviceRegistration(),
|
||||
keystore.getMasterPrivateExtendedKey() == null ? null : keystore.getMasterPrivateExtendedKey().getId(),
|
||||
keystore.getSeed() == null ? null : keystore.getSeed().getId(), wallet.getId(), i);
|
||||
keystore.setId(id);
|
||||
|
|
|
@ -25,6 +25,7 @@ public class KeystoreMapper implements RowMapper<Keystore> {
|
|||
keystore.setKeyDerivation(new KeyDerivation(rs.getString("keystore.masterFingerprint"), rs.getString("keystore.derivationPath")));
|
||||
keystore.setExtendedPublicKey(rs.getString("keystore.extendedPublicKey") == null ? null : ExtendedKey.fromDescriptor(rs.getString("keystore.extendedPublicKey")));
|
||||
keystore.setExternalPaymentCode(rs.getString("keystore.externalPaymentCode") == null ? null : PaymentCode.fromString(rs.getString("keystore.externalPaymentCode")));
|
||||
keystore.setDeviceRegistration(rs.getBytes("keystore.deviceRegistration"));
|
||||
|
||||
if(rs.getBytes("masterPrivateExtendedKey.privateKey") != null) {
|
||||
MasterPrivateExtendedKey masterPrivateExtendedKey = new MasterPrivateExtendedKey(rs.getBytes("masterPrivateExtendedKey.privateKey"), rs.getBytes("masterPrivateExtendedKey.chainCode"));
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.google.zxing.qrcode.QRCodeWriter;
|
|||
import com.sparrowwallet.drongo.KeyPurpose;
|
||||
import com.sparrowwallet.drongo.OutputDescriptor;
|
||||
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.KeystoreSource;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
|
@ -234,7 +235,10 @@ public class ReceiveController extends WalletFormController implements Initializ
|
|||
} else {
|
||||
Device actualDevice = possibleDevices.get(0);
|
||||
Hwi.DisplayAddressService displayAddressService = new Hwi.DisplayAddressService(actualDevice, "", wallet.getScriptType(), addressDescriptor,
|
||||
OutputDescriptor.getOutputDescriptor(walletForm.getWallet()), walletForm.getWallet().getFullName());
|
||||
OutputDescriptor.getOutputDescriptor(walletForm.getWallet()), walletForm.getWallet().getFullName(), getDeviceRegistration(actualDevice));
|
||||
displayAddressService.setOnSucceeded(successEvent -> {
|
||||
updateDeviceRegistrations(actualDevice, displayAddressService.getNewDeviceRegistrations());
|
||||
});
|
||||
displayAddressService.setOnFailed(failedEvent -> {
|
||||
Platform.runLater(() -> {
|
||||
DeviceDisplayAddressDialog dlg = new DeviceDisplayAddressDialog(wallet, addressDescriptor);
|
||||
|
@ -252,6 +256,26 @@ public class ReceiveController extends WalletFormController implements Initializ
|
|||
}
|
||||
}
|
||||
|
||||
private byte[] getDeviceRegistration(Device device) {
|
||||
Optional<Keystore> optKeystore = getWalletForm().getWallet().getKeystores().stream()
|
||||
.filter(keystore -> keystore.getKeyDerivation().getMasterFingerprint().equals(device.getFingerprint()) && keystore.getDeviceRegistration() != null).findFirst();
|
||||
return optKeystore.map(Keystore::getDeviceRegistration).orElse(null);
|
||||
}
|
||||
|
||||
private void updateDeviceRegistrations(Device device, Set<byte[]> newDeviceRegistrations) {
|
||||
if(!newDeviceRegistrations.isEmpty()) {
|
||||
List<Keystore> registrationKeystores = getDeviceRegistrationKeystores(device);
|
||||
if(!registrationKeystores.isEmpty()) {
|
||||
registrationKeystores.forEach(keystore -> keystore.setDeviceRegistration(newDeviceRegistrations.iterator().next()));
|
||||
EventManager.get().post(new KeystoreDeviceRegistrationsChangedEvent(getWalletForm().getWallet(), registrationKeystores));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Keystore> getDeviceRegistrationKeystores(Device device) {
|
||||
return getWalletForm().getWallet().getKeystores().stream().filter(keystore -> keystore.getKeyDerivation().getMasterFingerprint().equals(device.getFingerprint())).toList();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
if(currentEntry != null) {
|
||||
label.textProperty().unbindBidirectional(currentEntry.labelProperty());
|
||||
|
|
|
@ -640,6 +640,13 @@ public class WalletForm {
|
|||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void keystoreDeviceRegistrationsChanged(KeystoreDeviceRegistrationsChangedEvent event) {
|
||||
if(event.getWallet() == wallet) {
|
||||
Platform.runLater(() -> EventManager.get().post(new WalletDataChangedEvent(wallet)));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletTabsClosed(WalletTabsClosedEvent event) {
|
||||
for(WalletTabData tabData : event.getClosedWalletTabData()) {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
alter table keystore add column deviceRegistration varbinary(32) after externalPaymentCode;
|
Loading…
Reference in a new issue