cardano-sl
cardano-sl copied to clipboard
export legacy wallets in a friendly way for automatic import in cardano-wallet
Description
- f63761ebe5a9edcbf01310f7ebf263f7a5f0e12b implement export-wallets for Byron reboot migration This little command-line tool extract the name and encrypted XPrv of known wallets and spits them back in a user-friendly format on stdout.
The format matches exactly the JSON format expected by the cardano-wallet for restoring legacy wallet from xprv.
-
148076e40c6393c022b0d60e9fa0c0bd50d07fab Return 'null' for 'passphrase_hash' when there's no passphrase set
$ export-wallets --mainnet --wallet-db-path state-wallet-mainnet/wallet-db --keyfile state-wallet-mainnet/secret.key
[
{
"encrypted_root_private_key": "b824f5268bdf05d783b0c594936e51c65d6d155021ddcbdb3370bb7a51caba4bedece046fc9d12143ce900d8238e444904e08a85af80299b012e7f12030a2ab6d3d1cf6fc565476c17326baebddcf31c5e3409ba697b9bf04ac588cb59868c22d043e0237a3ba2eb167d1504dd93f788fc4a72503a261cf338ad7e4cf8837aff",
"name": "My Wallet",
"passphrase_hash": null
},
{
"encrypted_root_private_key": "5da58210e3f6e2b1e24659158554f91bb5970202ba0e0cd2d32767ce3c2893c29515a5469493f6133857c0dc86f127aa70e46d047424e07f7221715357bfb35052aed22cefd53ea7d75b5804add54e653c21e03ffd7c01cb8f6c033f7d383cc127046148d89a1828b76bdb46b56f6fec06d31b5b412dc68ab4b5ffa2418e7e8d",
"name": "My Wallet",
"passphrase_hash": "31347c387c317c57434478796c7064635a736853367a6d3150766d54456a34667038795a34623145557868627765347a36596645413d3d7c48544739445a456d4f2b79535a6a705a76553145614d795a437170732b43347330704738414562745638773d"
}
]
Linked issue
Type of change
- [ ] 🐞 Bug fix (non-breaking change which fixes an issue)
- [x] 🛠 New feature (non-breaking change which adds functionality)
- [ ] ⚠️ Breaking change (fix or feature that would cause existing functionality to change)
- [ ] 🏭 Refactoring that does not change existing functionality but does improve things like code readability, structure etc
- [ ] 🔨 New or improved tests for existing code
- [ ] ⛑ git-flow chore (backport, hotfix, etc)
Developer checklist
- [ ] I have read the style guide document, and my code follows the code style of this project.
- [ ] If my code deals with exceptions, it follows the guidelines.
- [ ] I have updated any documentation accordingly, if needed. Documentation changes can be reflected in opening a PR on cardanodocs.com, amending the inline Haddock comments, any relevant README file or one of the document listed in the docs directory.
- [ ] CHANGELOG entry has been added and is linked to the correct PR on GitHub.
Testing checklist
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.
QA Steps
Screenshots (if available)
How to merge
Send the message bors r+
to merge this PR. For more information, see
docs/how-to/bors.md
.
@cleverca22 how do you suggest we invoke and use this util in Daedalus? We need to pack it with the installer... cc @darko-mijic
@KtorZ can it happen that the script only extracts some of the wallets and not all of them? Are there any errors that could surprise us in this process?
@nikolaglumac I don't expect this script to error, provided the filepath given to it are correct. I kept the option name the same so that they exactly match those of cardano-node
and I am sure devops can wire the cables ^.^
As for your second question:
@KtorZ can it happen that the script only extracts some of the wallets and not all of them?
The only case I can think of (and for which I've added some logging), even though I doubt it'll happen, would be if there are secret keys in the keystore but no corresponding wallet in the acid-state file. I'd expect these two data store to be somewhat consistent with each other, but I am also not discarding the possibility of the keystore having references to keys that matches no wallet...
Anyway, if a wallet is being returned by the legacy API, it'll show up.
@cleverca22 how do you suggest we invoke and use this util in Daedalus? We need to pack it with the installer...
If I may, I'd suggest running this little with the installer itself, such that all wallets get exported to disk as a json file. Then, the new Daedalus can simply read that file and submit the exported wallets directly to the new backend.
The only case I can think of (and for which I've added some logging), even though I doubt it'll happen, would be if there are secret keys in the keystore but no corresponding wallet in the acid-state file. I'd expect these two data store to be somewhat consistent with each other, but I am also not discarding the possibility of the keystore having references to keys that matches no wallet...
@KtorZ but what would happen in this case? Will it throw an error or simply return just the wallets which are both within secrets.key and the acid state?
If I may, I'd suggest running this little with the installer itself, such that all wallets get exported to disk as a json file. Then, the new Daedalus can simply read that file and submit the exported wallets directly to the new backend.
We would like to avoid writing this info to the hard drive. cc @darko-mijic
[nix-shell:~/iohk/daedalus-turtle]$ export-wallets --mainnet --keyfile ~/.local/share/Daedalus/mainnet/Secrets/secret.key --wallet-db-path ~/.local/share/Daedalus/mainnet/Wallet-acid
No wallet for id: Ae2tdPwUPEZGEqm4BX_______1fN5kodQRmGe3
[]
[nix-shell:~/iohk/daedalus-turtle]$ mkdir temp
[nix-shell:~/iohk/daedalus-turtle]$ export-wallets --mainnet --keyfile ~/.local/share/Daedalus/mainnet/Secrets/secret.key --wallet-db-path temp
No wallet for id: Ae2tdPwUPEZGEqm4BX_______1fN5kodQRmGe3
[]
my wallet happens to be missing in the acid-state db, and the tool fails identically for both the db, and an empty dir
it would help if it could return:
{
"encrypted_root_private_key": "...",
"name": null,
"passphrase_hash": ...,
"warnings":["couldnt find matching wallet in acid-state"]
},
We would like to avoid writing this info to the hard drive.
It's already on the hard drive...
It's already on the hard drive...
I know, but it is not provided in such a way I guess. We will think about this option. cc @darko-mijic
@KtorZ if a wallet is missing from the acid-state db then surely it is not visible in Daedalus right?
@KtorZ if a wallet is missing from the acid-state db then surely it is not visible in Daedalus right?
Correct.
:paperclip: export-wallets-qa.tar.gz
export-wallets-qa/README.md
How To Use
:bulb: Options behaves identically to the node's eponymous options.
Mainnet
- The keystore from
state-wallet-mainnet
contains 4 keys, 2 of which corresponds to wallets in the acid-state database. - The acid-state database from
state-wallet-mainnet
contains two wallets created using the inputs from the Annex.
./export-wallets --mainnet \
--wallet-db-path ./state-wallet-mainnet/wallet-db \
--keyfile ./state-wallet-mainnet/secret.key
Testnet
- The keystore from
state-wallet-testnet
contains 2 keys corresponding to the 2 wallets in the acid-state database. - The acid-state database from
state-wallet-testnet
contains two wallets created using the inputs from the Annex.
./export-wallets --testnet 1097911063 \
--wallet-db-path ./state-wallet-testnet/wallet-db \
--keyfile ./state-wallet-testnet/secret.key
Annex
wallet with passphrase
{
"assuranceLevel": "strict",
"backupPhrase": [
"inhale",
"arm",
"pilot",
"fitness",
"ceiling",
"october",
"donate",
"between",
"language",
"all",
"limit",
"taxi"
],
"name": "亜哀うめるтУуФф",
"operation": "create",
"spendingPassword": "0000000000000000000000000000000000000050415441544520504154415445"
}
wallet without passphrase
{
"assuranceLevel": "strict",
"backupPhrase": [
"marriage",
"blouse",
"orbit",
"quarter",
"treat",
"series",
"release",
"sing",
"lava",
"spice",
"surface",
"rule"
],
"name": "WALLET 💜 💛 💚 💙",
"operation": "create",
"spendingPassword": null
}
LGTM.
- I found that when Daedalus is up, this script is not possible to use (I think this might be good to know):
$ ./export-wallets --mainnet --wallet-db-path ~/.local/share/Daedalus/mainnet/Wallet --keyfile ~/.local/share/Daedalus/mainnet/Secrets/secret.key
export-wallets: StateIsLocked "/home/piotr/.local/share/Daedalus/mainnet/Wallet-acid/open.lock"
- I have made some tests also directly on Daedalus database on Linux..
$ ./export-wallets --mainnet --wallet-db-path ~/.local/share/Daedalus/mainnet/Wallet --keyfile ~/.local/share/Daedalus/mainnet/Secrets/secret.key
[
{
"encrypted_root_private_key": "65b0a8a43341078aa25e499457902bed3e7d63a74d6079a563e08f0a7e2854dc918ed1b98c2e262d837276cce3fa8c3825ae5aa0f24535168b597127bd62461b323dba7f9c2beb5dbf24b528b6981e83c2e21a4aa7684746d86b763191aada4f980a05ec790380f10c8d05df95750bdd482870ed294ffc2f6356da69d0287950",
"name": "`~`!@#$%^&*()_+-=<>,./?;':\"\"'{}[]\\|❤️ 💔 💌 💕 💞 💓 💗 💖 💘 💝 💟 💜 💛 💚 💙0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟🇺🇸🇷🇺🇸 🇦🇫🇦🇲🇸",
"passphrase_hash": "31347c387c317c57434338486c412b6e3834734c426e55427450447a4a59536e6a7742794b4f5662387171534977476f48557a7a773d3d7c497944797147756c6b493733494f4d66544e615034572b7447396b78485a54665870795a572f436733316f3d"
},
{
"encrypted_root_private_key": "1dd082ced0f831afe5ab065450ed136ac06e412adf0a06edfdca5fb21eb777df68e62e7b2b3fee8c7d9c21e8cf1dd7bf655b7684d7149f52d1f6ec0f723aab9436db71be813be0462930225922f1d11a544db877b1a5bd5dddfc73e469ad3980fd27e3436a49ba158810ecbb1612006d79eac41dd8ebe0ab67d184fed8d7479f",
"name": "亜哀挨愛曖悪握圧扱宛嵐安案暗以衣位囲医依委威為畏胃尉異移萎偉椅彙意違維慰 遺緯域育一壱逸茨芋引印因咽姻員院淫陰飲隠韻右宇羽雨唄鬱畝浦運雲永泳英映栄\n営詠影鋭衛易疫益液駅悦越謁 閲円延沿炎怨宴媛援園煙猿遠鉛塩演縁艶汚王凹\r\n央応往押旺欧殴桜翁奥横岡屋億憶臆虞乙俺卸音恩温穏下化火加 可仮何花佳価果河苛科架夏家荷華菓貨渦過嫁暇禍靴寡歌箇稼課蚊牙瓦我画芽賀雅餓介回灰会快戒改怪拐悔海界 皆械絵開階塊楷解潰壊懐諧貝外劾害崖涯街慨蓋該概骸垣柿各角拡革格核殻郭覚較隔閣確獲嚇穫学岳楽額顎掛潟 括活喝渇割葛滑褐轄且株釜鎌刈干刊甘汗缶\r",
"passphrase_hash": "31347c387c317c574343746d337732414a464a4f6a326d506c334a544c5a3533427057366263457838394c76344c2b6e70545752673d3d7c427359646477565656304b786c567a46666264706a69495a6f654f505430565a624f4a47492f42537771593d"
},
{
"encrypted_root_private_key": "edb251441107f6d70613ab89eec818753ff4390ffdd446e9ffe431becb7b22d8e7e5a6875624548a249f489c333f397b8da55fcba32d764166f2b402e5086525dfecf921cac5281a07e607297b20541818e166ff4e127fac8c837860703e7220820256aef6576cd2053a10b64c82d58d471262bbf2c95bb9f246d41daf2b3713",
"name": "ثم نفس سقطت وبالتحديد،, جزيرتي باستخدام أن دنو. إذ هنا؟ الستار وتنصيب كان. أهّل ايطاليا، بريطانيا-فرنسا قد أخذ. سليمان، إتفاقية بين ما, يذكر الحدود أي بعد, معاملة بولندا، الإطلاق عل إيو.",
"passphrase_hash": "31347c387c317c5743415a67386d325345577275505874656452636277583637524e55793763722f7778414b5a494e466755596b513d3d7c694d6f66593074445164794b375549745a796c6677392b425831626f6f73433672523532684630305932553d"
},
{
"encrypted_root_private_key": "75bea242039182aea099c443ba601572fc68e03eba46309353453148671695dc458df8b715e515784120aa14da9ea671b5b10b8cb29528dac0ecd593812d4ac084443bfc8dace09c03e96fbef07d8853ca7774f633d97a91eb2243c9bcf1a6f36290b278271c8a782b5bfc42aef90984ab2fecad286669f51482871546d655ad",
"name": "aąbcćdeęfghijklłmnoóprsś\r\ntuvwyzżźAĄBCĆDEĘFGHIJKLŁMNOP\rRSŚTUVWYZŻŹ",
"passphrase_hash": "31347c387c317c57434374303455384757434b3171646b7439635a2b65336e3343595837487a4831593531487a5166795141644b673d3d7c4a6837513162473550647a783442372b50414374776676494a3158616f757642334b5a6b5033493763676f3d"
},
{
"encrypted_root_private_key": "5d80b8cf019053034580f81474da1c490873679891581f0dce6d341183df31c49d79de812c33a46a7f79eaf2f436c618d3efc45c70d9a18af57fb2282dd93775bdd4035be38887881fb3813f2c317a82a749ce135275c8ec5567830213a3cf0e55396dcf70a6002a075eb1b550d6ba1955781a8b57c68629721c7669eb7b0513",
"name": "АаБбВвГгДдЕеЁёЖжЗз ИиЙйКкЛлМмНнО оПпРрСсТтУуФф ХхЦцЧчШшЩщЪъ ЫыЬьЭэЮюЯяІ ѢѲѴѵѳѣі",
"passphrase_hash": "31347c387c317c5743445145374e6156312b78304455325930736737667335314c6f366c5648682b4435744e74334559434c367a513d3d7c454d6471313478504f67614979573945355539426973337a49326e2f5a787a532f587230483746473377593d"
}
]
@ktorz This can't be done at install time because the installer doesn't know the path to the user state directory. The installation happens globally but the state is tied to the user home directory/AppData. What probably makes sense is before launching the launcher, check if a new state directory exists. If it does, do nothing, if it doesn't check if a legacy state dir exists, if it does run the utility. Then proceed as normal with launching node/wallet.
Daedalus integration is now complete: https://github.com/input-output-hk/daedalus/pull/1905
@nikolaglumac updated.
- now return a
id
(new format) for each wallet - wallets without metadata are returned, but
name
is set tonull
Here's an example on the previously generated databases:
$ export-wallets --mainnet --wallet-db-path ./state-wallet-mainnet/wallet-db --keyfile state-wallet-mainnet/secret.key
[
{
"encrypted_root_private_key": "f6e79f49b8999a39d7e970e42d0a91224ecacefc3aa1edb342f34eb8bc6c2fc63e743b862b312a6f92ba0161d4d53c3ee5a2bd8085476d9575765c49dceecbe54b34ec47daf9b7ebc6bdb706622616451c000e85ba81c7449ae436a8cbbf3aab98e5cc704977bd11bb0ba8d5b5571a705704cb9334d27a048532eab49a698c2d",
"name": "亜哀うめるтУуФф",
"id": "5c409c7e4fddfa99d11c0345dc75e514d2a0faad",
"passphrase_hash": "31347c387c317c5743413633702f6a487a5777575278756756344e316854793470646c6d4f76665177653863775a575472784f79773d3d7c796341722f61326f4f777a736e4e746f4e655049416e4f6b7978426549494a6b59623039574b564a7159493d"
},
{
"encrypted_root_private_key": "38e8de9c583441213fe34eecc4e28265267466877ba4048e3ab1fa9956366947aefaf5ba9779db67eead7fc9cd1354b994a5d8d9cd40ab874bfeb1b33649280cd33651377731e0e59e0233425a55257782c5adaa768da0567f43c1c6c0c18766ed0a547bb34eb472c120b170a8640279832ddf18002887f03c15dea59705422d",
"name": "WALLET 💜 💛 💚 💙",
"id": "05377e480bd68a2da04f00c39ce5886e4746ed1f",
"passphrase_hash": "31347c387c317c574342652b796362417576356c2b4258676a344a314c6343675375414c2f5653393661364e576a2b7550766655513d3d7c6f784636654939734151444e6f38395147747366324e653937426338372b484b6b4137756772752f5970673d"
},
{
"encrypted_root_private_key": "b824f5268bdf05d783b0c594936e51c65d6d155021ddcbdb3370bb7a51caba4bedece046fc9d12143ce900d8238e444904e08a85af80299b012e7f12030a2ab6d3d1cf6fc565476c17326baebddcf31c5e3409ba697b9bf04ac588cb59868c22d043e0237a3ba2eb167d1504dd93f788fc4a72503a261cf338ad7e4cf8837aff",
"name": null,
"id": "781647fe8876d7e09e3d2da23658482deebfb0d1",
"passphrase_hash": "31347c387c317c5743436e774771435944796e4233684d477168706234556c64392b6a6844506b3836314e5a773865382b637954673d3d7c524e53642f35537a51704857593148375161594b3466423045466f765659564830666b2b4d6c53454d64733d"
},
{
"encrypted_root_private_key": "5da58210e3f6e2b1e24659158554f91bb5970202ba0e0cd2d32767ce3c2893c29515a5469493f6133857c0dc86f127aa70e46d047424e07f7221715357bfb35052aed22cefd53ea7d75b5804add54e653c21e03ffd7c01cb8f6c033f7d383cc127046148d89a1828b76bdb46b56f6fec06d31b5b412dc68ab4b5ffa2418e7e8d",
"name": null,
"id": "0a436b1142929758a54f6008ff558ff8fa264e76",
"passphrase_hash": "31347c387c317c57434478796c7064635a736853367a6d3150766d54456a34667038795a34623145557868627765347a36596645413d3d7c48544739445a456d4f2b79535a6a705a76553145614d795a437170732b43347330704738414562745638773d"
}
]
@KtorZ you are a true ⭐️ This looks awesome 🎉
@KtorZ did you also set it so that secret.key is mandatory and wallet-acid-db optional?
@KtorZ did you also set it so that secret.key is mandatory and wallet-acid-db optional?
From the command-line options you mean? No. I didn't quite get that this is what you wanted. Although, passing /dev/null
as a db-path would do the same.
From the command-line options you mean? No. I didn't quite get that this is what you wanted. Although, passing
/dev/null
as a db-path would do the same.
Well we will always be passing a path - but it may not be valid in some cases (e.g. when user only has secret.key...
Also, side-note: I couldn't get you the balance. This requires setting up a whole 'NodeStateAdaptor' which basically needs the entire node configuration and is therefore an much heavier setup. I've started pulling the thread a bit but if this is really something you'd want at some point, we'll need to allocate a day or two to work this out.
Also, side-note: I couldn't get you the balance. This requires setting up a whole 'NodeStateAdaptor' which basically needs the entire node configuration and is therefore an much heavier setup. I've started pulling the thread a bit but if this is really something you'd want at some point, we'll need to allocate a day or two to work this out.
@KtorZ this could be handled on Daedalus end once we extract the wallets if you would be able to get us an endpoint which takes the wallet-id and returns the wallet balance.
@KtorZ please let me know once this version is ready so @disassembler can add it to Daedalus 🙏
@KtorZ @disassembler any ETA on when this will be ready?
you would be able to get us an endpoint which takes the wallet-id and returns the wallet balance.
This won't probably happen, unless the node makes the full Ledger UTxO available through the local state query protocol. Something to be checked with them.
@KtorZ @disassembler any ETA on when this will be ready?
Yes, 2 days ago.
@nikolaglumac
-
13f5729585e298643ecfc757fbb2da125a90e057 :round_pushpin: make acid-state database optional from the command-line
-
c1815b7dce5fb71d997bd3a94c4c5ccf2c9a9a94 :round_pushpin: show in the JSON export whether a passphrase is empty or not
$ export-wallets --mainnet --keyfile state-wallet-mainnet/secret.key [ { "encrypted_root_private_key": "18b952a968ccbac047f9c227dd05fced84ba85a1f83daf54f0a3c0de3e5664502678b1ba34a0b44ce22a2b54f39830570bdfeeed0a2f8586aa988fa5399987252b81f30b3c1e4c39e1edb16a4c7975eea106fc5d12d3b4fa516b9332f777defba949a42ef709a2d2f5c64057e99987c545b6e014f8cd9502bd5b800ddc6e10b7", "is_passphrase_empty": true, "name": null, "id": "f1aaaf1e1e6db1255ec2d59d50c5db892ff20330", "passphrase_hash": "31347c387c317c5743424875746242496c6a66734d764934314a30727a79663076657375724954796376766a793150554e377452673d3d7c54753434596d6e547957546c5759674a3164494f7974474a7842632b432f7862507657382b5135356a38303d" }, { "encrypted_root_private_key": "6d9e6a5135d373fb62eadaf02bc8f5ecf35148806721c704c6dbfcf818ff958f1fc4a933542c423f5b8621cfe6404c4b208f2c96f6246082dffbc9fb1029a79ed33651377731e0e59e0233425a55257782c5adaa768da0567f43c1c6c0c18766ed0a547bb34eb472c120b170a8640279832ddf18002887f03c15dea59705422d", "is_passphrase_empty": false, "name": null, "id": "05377e480bd68a2da04f00c39ce5886e4746ed1f", "passphrase_hash": "31347c387c317c574342652b796362417576356c2b4258676a344a314c6343675375414c2f5653393661364e576a2b7550766655513d3d7c2f376738486c59723174734e394f6e4e753253302b6a65515a6b5437316b45414941366a515867386539493d" } ]
Works great @KtorZ. Thanks!
Hey, just an FYI that we've given thoughts about importing/exporting wallet files for Yoroi and this won't be compatible with our format since ours will be more generic as opposed to the straight-forward approach you're taking. Maybe it doesn't matter since I think this is just your temporary solution for legacy wallets.
@SebastienGllmt yes, this is simply to enable a smooth migration of the legacy cardano-sl. This clearly abuses the old scheme and the way the HD payload was constructed and encrypted.
For a proper import/export wallet, we should definitely agree on a format via a CIP :)
export-wallets-qa/README.md
How To Use
💡 Options behaves identically to the node's eponymous options.
Mainnet
- The keystore from
state-wallet-mainnet
contains 4 keys, 2 of which corresponds to wallets in the acid-state database.- The acid-state database from
state-wallet-mainnet
contains two wallets created using the inputs from the Annex../export-wallets --mainnet \ --wallet-db-path ./state-wallet-mainnet/wallet-db \ --keyfile ./state-wallet-mainnet/secret.key
Testnet
- The keystore from
state-wallet-testnet
contains 2 keys corresponding to the 2 wallets in the acid-state database.- The acid-state database from
state-wallet-testnet
contains two wallets created using the inputs from the Annex../export-wallets --testnet 1097911063 \ --wallet-db-path ./state-wallet-testnet/wallet-db \ --keyfile ./state-wallet-testnet/secret.key
Annex
wallet with passphrase
{ "assuranceLevel": "strict", "backupPhrase": [ "inhale", "arm", "pilot", "fitness", "ceiling", "october", "donate", "between", "language", "all", "limit", "taxi" ], "name": "亜哀うめるтУуФф", "operation": "create", "spendingPassword": "0000000000000000000000000000000000000050415441544520504154415445" }
wallet without passphrase
I used the testnet data name=亜悲めるтУуФф wallet data in the example to import into the new byron wallet, but when I created the address, 0000000000000000000000000000000000000050415441544520504154415445 was passed as the passphrase, and then the following error was returned:
{"code":"wrong_encryption_passphrase","message":"The given encryption passphrase doesn't match the one I use to encrypt the root private key of the given wallet: 5c409c7e4fddfa99d11c0345dc75e514d2a0faad"}
- I want to know what is the password of the testnet sample data?
- In addition, can the latest version of export-wallet provide download packages directly? @KtorZ