From 5817b80c947b506ca042f4a7a9805b0a7849fcab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Job=20Evers=E2=80=90Meltzer?=
 <jobevers@users.noreply.github.com>
Date: Sun, 17 Jul 2016 20:17:42 -0500
Subject: [PATCH] script to migrate private keys to lbrycrd (#79)

* script to migrate private keys to lbrycrd
* handle compressed keys too
* validate private key actually gets added
* add lbrycrdd and lbrycrd-cli checks
* exit if no wallet path
* move each imported address into the default account
---
 scripts/migrate_lbryum_to_lbrycrd.py | 113 +++++++++++++++++++++++++++
 1 file changed, 113 insertions(+)
 create mode 100644 scripts/migrate_lbryum_to_lbrycrd.py

diff --git a/scripts/migrate_lbryum_to_lbrycrd.py b/scripts/migrate_lbryum_to_lbrycrd.py
new file mode 100644
index 000000000..19391ba75
--- /dev/null
+++ b/scripts/migrate_lbryum_to_lbrycrd.py
@@ -0,0 +1,113 @@
+import argparse
+import hashlib
+import json
+import subprocess
+import sys
+
+import base58
+
+from lbryum import SimpleConfig, Network
+from lbryum.wallet import WalletStorage, Wallet
+from lbryum.commands import known_commands, Commands
+from lbryum import lbrycrd
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--wallet', help='path to lbryum wallet')
+    args = parser.parse_args()
+
+    ensureCliIsOnPathAndServerIsRunning()
+
+    wallet = getWallet(args.wallet)
+    addresses = wallet.addresses(True)
+    for addr in addresses[:-1]:
+        printBalance(wallet, addr)
+        saveAddr(wallet, addr)
+    # on the last one, rescan.  Don't rescan early for sake of efficiency
+    addr = addresses[-1]
+    printBalance(wallet, addr)
+    saveAddr(wallet, addr, "true")
+
+
+def ensureCliIsOnPathAndServerIsRunning():
+    try:
+        output = subprocess.check_output(['lbrycrd-cli', 'getinfo'])
+    except OSError:
+        print 'Failed to run: lbrycrd-cli needs to be on the PATH'
+        exit(1)
+    except subprocess.CalledProcessError:
+        print 'Failed to run: could not connect to the lbrycrd server.'
+        print 'Make sure it is running and able to be connected to.'
+        print 'One way to do this is to run:'
+        print '      lbrycrdd -server -printtoconsole'
+        exit(1)
+
+
+def validateAddress(addr):
+    raw_output = subprocess.check_output(
+        ['lbrycrd-cli', 'validateaddress', addr])
+    output = json.loads(raw_output)
+    if not output['isvalid']:
+        raise Exception('Address {} is not valid'.format(addr))
+    if not output['ismine']:
+        raise Exception('Address {} is not yours'.format(addr))
+
+
+def printBalance(wallet, addr):
+    balance = getBalance(wallet, addr)
+    print 'Importing private key for %s with balance %s' % (addr, balance)
+
+
+def getBalance(wallet, addr):
+    return sum(wallet.get_addr_balance(addr))
+
+
+def getWallet(path=None):
+    if not path:
+        config = SimpleConfig()
+        path = config.get_wallet_path()
+    storage = WalletStorage(path)
+    if not storage.file_exists:
+        print "Failed to run: No wallet to migrate"
+        exit(1)
+    return Wallet(storage)
+
+
+def saveAddr(wallet, addr, rescan="false"):
+    keys = wallet.get_private_key(addr, None)
+    assert len(keys) == 1, 'Address {} has {} keys.  Expected 1'.format(addr, len(keys))
+    key = keys[0]
+    # copied from lbrycrd.regenerate_key
+    b = lbrycrd.ASecretToSecret(key)
+    pkey = b[0:32]
+    is_compressed = lbrycrd.is_compressed(key)
+    wif = pkeyToWif(pkey, is_compressed)
+    subprocess.check_call(
+        ['lbrycrd-cli', 'importprivkey', wif, "lbryum import", rescan])
+    validateAddress(addr)
+    # during the import the account gets set to the label, but lbry
+    # needs the address to be in the default account
+    subprocess.check_call(['lbrycrd-cli', 'setaccount', addr, '""'])
+
+
+def pkeyToWif(pkey, compressed):
+    # Follow https://en.bitcoin.it/wiki/Wallet_import_format
+    # to convert from a private key to the wallet import format
+    prefix = '\x1c'
+    wif = prefix + pkey
+    if compressed:
+        wif += '\x01'
+    intermediate_checksum = hashlib.sha256(wif).digest()
+    checksum = hashlib.sha256(intermediate_checksum).digest()
+    wif = wif + checksum[:4]
+    return base58.b58encode(wif)
+
+
+def wifToPkey(wif):
+    pkey = base58.b58decode(wif)
+    return pkey[1:-4]
+
+
+if __name__ == '__main__':
+    sys.exit(main())