sparrowwallet / sparrow

Desktop Bitcoin Wallet focused on security and privacy. Free and open source.
https://sparrowwallet.com/
Apache License 2.0
1.37k stars 192 forks source link

Electrum wallet import of old keystore type fails #1080

Open st3vvv opened 1 year ago

st3vvv commented 1 year ago

I encounter this error on Ubuntu 20.04 LTS when trying to import an Electrum wallet in to Sparrow 1.7.8 - Looking through closed issues, i observe issue #881 -

in which Electrum import fails with the same error, on Mac Os.

However, in that case the wallet had keys imported into it after creation, whereas i am unaware of having imported keys into the wallet i am attempting to use.

It is an old wallet though - from 2013 - i can open it in the latest Electrum ( 4.4.6), and in the wallet info tab, it lists the seed, and keystore type as 'old' - the wallet type is 'standard', and the script type p2pkh.

Could the 'old' nature of the wallet be the reason for import failure ?

Here is the error log, and a wallet info screenshot -

wallet info 1 Screenshot from 2023-08-26 13-10-52

2023-08-26 12:55:31,988 ERROR [JavaFX Application Thread] c.s.s.c.FileImportPane [null:-1] Error importing file com.sparrowwallet.sparrow.io.ImportException: Error importing Electrum Wallet at com.sparrowwallet.sparrow@1.7.8/com.sparrowwallet.sparrow.io.Electrum.importWallet(Unknown Source) at com.sparrowwallet.sparrow@1.7.8/com.sparrowwallet.sparrow.control.FileWalletImportPane.importFile(Unknown Source) at com.sparrowwallet.sparrow@1.7.8/com.sparrowwallet.sparrow.control.FileImportPane.importFile(Unknown Source) at com.sparrowwallet.sparrow@1.7.8/com.sparrowwallet.sparrow.control.FileImportPane.importFile(Unknown Source) at com.sparrowwallet.sparrow@1.7.8/com.sparrowwallet.sparrow.control.FileImportPane.lambda$createButton$3(Unknown Source) at javafx.base@18/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventUtil.fireEvent(Unknown Source) at javafx.base@18/javafx.event.Event.fireEvent(Unknown Source) at javafx.graphics@18/javafx.scene.Node.fireEvent(Unknown Source) at javafx.controls@18/javafx.scene.control.Button.fire(Unknown Source) at javafx.controls@18/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(Unknown Source) at javafx.controls@18/com.sun.javafx.scene.control.inputmap.InputMap.handle(Unknown Source) at javafx.base@18/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventUtil.fireEvent(Unknown Source) at javafx.base@18/javafx.event.Event.fireEvent(Unknown Source) at javafx.graphics@18/javafx.scene.Scene$MouseHandler.process(Unknown Source) at javafx.graphics@18/javafx.scene.Scene.processMouseEvent(Unknown Source) at javafx.graphics@18/javafx.scene.Scene$ScenePeerListener.mouseEvent(Unknown Source) at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source) at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source) at java.base/java.security.AccessController.doPrivileged(Unknown Source) at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(Unknown Source) at javafx.graphics@18/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(Unknown Source) at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(Unknown Source) at javafx.graphics@18/com.sun.glass.ui.View.handleMouseEvent(Unknown Source) at javafx.graphics@18/com.sun.glass.ui.View.notifyMouse(Unknown Source) at javafx.graphics@18/com.sun.glass.ui.gtk.GtkApplication.enterNestedEventLoopImpl(Native Method) at javafx.graphics@18/com.sun.glass.ui.gtk.GtkApplication._enterNestedEventLoop(Unknown Source) at javafx.graphics@18/com.sun.glass.ui.Application.enterNestedEventLoop(Unknown Source) at javafx.graphics@18/com.sun.glass.ui.EventLoop.enter(Unknown Source) at javafx.graphics@18/com.sun.javafx.tk.quantum.QuantumToolkit.enterNestedEventLoop(Unknown Source) at javafx.graphics@18/javafx.stage.Stage.showAndWait(Unknown Source) at javafx.controls@18/javafx.scene.control.HeavyweightDialog.showAndWait(Unknown Source) at javafx.controls@18/javafx.scene.control.Dialog.showAndWait(Unknown Source) at com.sparrowwallet.sparrow@1.7.8/com.sparrowwallet.sparrow.AppController.importWallet(Unknown Source) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) at java.base/java.lang.reflect.Method.invoke(Unknown Source) at com.sun.javafx.reflect.Trampoline.invoke(Unknown Source) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) at java.base/java.lang.reflect.Method.invoke(Unknown Source) at javafx.base@18/com.sun.javafx.reflect.MethodUtil.invoke(Unknown Source) at javafx.fxml@18/com.sun.javafx.fxml.MethodHelper.invoke(Unknown Source) at javafx.fxml@18/javafx.fxml.FXMLLoader$MethodHandler.invoke(Unknown Source) at javafx.fxml@18/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(Unknown Source) at javafx.base@18/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventUtil.fireEvent(Unknown Source) at javafx.base@18/javafx.event.Event.fireEvent(Unknown Source) at javafx.controls@18/javafx.scene.control.MenuItem.fire(Unknown Source) at javafx.controls@18/com.sun.javafx.scene.control.ContextMenuContent$MenuItemContainer.doSelect(Unknown Source) at javafx.controls@18/com.sun.javafx.scene.control.ContextMenuContent$MenuItemContainer.lambda$createChildren$12(Unknown Source) at javafx.base@18/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source) at javafx.base@18/com.sun.javafx.event.EventUtil.fireEvent(Unknown Source) at javafx.base@18/javafx.event.Event.fireEvent(Unknown Source) at javafx.graphics@18/javafx.scene.Scene$MouseHandler.process(Unknown Source) at javafx.graphics@18/javafx.scene.Scene.processMouseEvent(Unknown Source) at javafx.graphics@18/javafx.scene.Scene$ScenePeerListener.mouseEvent(Unknown Source) at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source) at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source) at java.base/java.security.AccessController.doPrivileged(Unknown Source) at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(Unknown Source) at javafx.graphics@18/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(Unknown Source) at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(Unknown Source) at javafx.graphics@18/com.sun.glass.ui.View.handleMouseEvent(Unknown Source) at javafx.graphics@18/com.sun.glass.ui.View.notifyMouse(Unknown Source) at javafx.graphics@18/com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method) at javafx.graphics@18/com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(Unknown Source) at java.base/java.lang.Thread.run(Unknown Source) Caused by: java.lang.NullPointerException: Cannot invoke "String.length()" because "" is null at com.sparrowwallet.drongo/com.sparrowwallet.drongo.protocol.Base58.decode(Unknown Source) at com.sparrowwallet.drongo/com.sparrowwallet.drongo.protocol.Base58.decodeChecked(Unknown Source) at com.sparrowwallet.drongo/com.sparrowwallet.drongo.ExtendedKey.fromDescriptor(Unknown Source) ... 126 common frames omitted

craigraw commented 1 year ago

Yes, unfortunately it appears that this Electrum wallet does not follow the common keystore format, probably because it is an old keystore type. I can try to add support for this, but I'll need to see the structure of the keystore (in JSON format). This is the format Electrum uses for unencrypted wallets.

An alternative might be to recreate this wallet in Electrum using the same seed, export/import the labels, and then try to import the new wallet file into Sparrow.

st3vvv commented 1 year ago

I had a go at opening the original wallet in Electrum 4.4.6, exporting the labels from the wallet, then using the seed to recreate the wallet in Electrum 4.4.6 and importing the labels from the original wallet, but it again fails with the same error when trying to import into sparrow.

One thing to note is that the original (2013) wallet is named electrum.dat, while the wallet created in the 4.4.6 release is called default_wallet.

I can open the electrum.dat file in text editor and see the wallet info - not sure what i can publicly show here, but i notice a couple of things scrolling through -

After a section of "contacts" reference to keystore -

"currency": "USD", "fee_per_kb": 20000, "frozen_addresses": [], "gap_limit": 5, "gui": "classic", "imported_keys": {}, "io_dir": "E:/electrum", "keystore": { "mpk": "MPK HERE", "seed": "SEED HERE=", "type": "old" },

    and a little further down, after a section of "labels" a seed version number -

     "language": "",
"litegui_theme": "Dark",
"num_zeros": 0,
"prioritized_addresses": [],
"proxy": null,
"seed_version": 18,
"server": "SERVER NAME HERE",
"spent_outpoints": 

Not sure if this is of any help.

The Electrum website seems to be the only place where older versions are available, and goes back to version 1.8 - the date on the download index says 2015, but the actual 1.8 onward versions date from late July 2013 - which would be about the right time i initially created the wallet - i can't say for sure which version i used though.

The download page -

https://electrum.org/#download

The index of previous releases -

https://download.electrum.org/

craigraw commented 1 year ago

Thanks for the info. It seems likely from their documentation that this is a pre-Electrum 2.0 seed, meaning it may be using the old mnemonic system in Electrum which I think is reflected in https://github.com/spesmilo/electrum/blob/a740a20fc2677d54e99fa981b7968b877a7b53a3/electrum/old_mnemonic.py

That said, I can't get Electrum 1.8 to run, so I can't be sure. If you can, please create a new wallet and attach the wallet file here?

st3vvv commented 1 year ago

I got version 1.8 portable to run on an old Windows 7 Service Pack 1 machine, and created a wallet, which is an electrum.dat file. I left the password blank. electrum.zip

craigraw commented 1 year ago

Thanks that's helpful - but we see in this file that the seed_version is actually 4, not 18 as in your file. It appears there have been a lot of versions!

So I now think the key is to focus on the keystore section you included above:

"keystore": {
"mpk": "MPK HERE",
"seed": "SEED HERE=",
"type": "old"
},

Question: Does the value in the seed field consist of space separated words, and are all those words in this list: https://github.com/spesmilo/electrum/blob/307cf25fd415c3d83139118c93d16f6e2e46b6db/electrum/old_mnemonic.py

st3vvv commented 1 year ago

The "seed" part seems to be comprised of 4 alphanumeric strings joined by plus signs(+), with the equals sign at the end.

The first part of the string is of 9 upper and lower case letters, the second part is 36 alphanumeric characters (with both upper and lower case letters), the third part is of 10 alphanumeric characters, with all lower case letters, and the fourth is of 28 alphanumeric characters, again with both upper and lower case letters.

I'll give a visual example here, in case that's not clear -

"seed": "TSaZbmJKL+bBET69PdNpiz9a3PVs42ADTd9GxAdycd5AGX+sdg7os1c6v+nzbPGADziW2XlJHdbc5tabss5zmT="

Note, there is a space between the : and the "

st3vvv commented 1 year ago

Out of curiosity i downloaded and created new wallets for releases 1.8 to 1.9.8.

The dat form of the wallet is present in 1.8, 1.8.1, and 1.9 - thereafter, the default_wallet type is present.

All releases i downloaded however, both dat and default_wallet type have a seed of 32 alphanumeric characters, with lower case letters, and all releases also share a seed version of 4.

I have no idea why my wallet is so different to this, as almost all the transactions in it date from 2013, with a couple of sends in Jan 2014, and not of my doing, a couple of the 1 satoshi spam sent to 2 addresses in July of 2017.

I have managed to find my old bitcointalk forum login and asked a question there in the Electrum board regarding this issue - let's see if anyone there can assist in this mystery.

https://bitcointalk.org/index.php?topic=5465852.msg62803144#msg62803144

craigraw commented 1 year ago

Did the suggestions there help?

st3vvv commented 1 year ago

The last two replies seem to help somewhat -

Regarding the unusual structure of my seed, it's suggested that base64 encoding is being used, as the commenter correctly points out that my wallet has a password, and suggests the seed would be encrypted as a result, and that alters the seed structure.

I tried this with a 1.8 client, creating a wallet without a password, and then adding one, and can confirm the seed changes from a single string to a longer string joined by plus signs and with an equals sign at the end. In retrospect, i should have considered this.

The other reply suggests that i must have opened the original electrum.dat wallet file in a later version of the client at some point, and that would be why it lists a later seed version in the wallet file - he gives an example of a wallet he created in 1.8, with a seed version 4, and how it reports a seed version of 18 when opened with 3.3.4 .

I also tried this myself with a freshly created, empty wallet, and can confirm that this is also occurred when opening a 1.8 wallet in 3.3.4 .

The wallet structure is different when opened with the 3.3.4 client, and lists the seed type as 'old', and the wallet type as 'standard' - interestingly, the wallet file also remains an electrum .dat file, and is not changed to a default_wallet type after opening in 3.3.4 .

we7u commented 11 months ago

FYI: This appears to be my issue as well: Can't import the Electrum backup file. This is a 2014-era wallet with a passphrase. During the import it reports: Cannot invoke "String.length()" because "\<parameter1>" is null

Rigadin commented 1 month ago

Unfortunately, I have exactly the same problem. I can't find a solution apart from making a (costly) transaction.