basicswap-prepare tries to initialise coin wallets from Particl mnemonic

Bitcoin 0.20: 'Cannot set a new HD seed while still in Initial Block Download.' Removed in 0.21
This commit is contained in:
tecnovert
2020-12-04 01:46:01 +02:00
parent 3bbb483a0a
commit 5a163e0f86
18 changed files with 754 additions and 93 deletions

View File

@@ -49,6 +49,9 @@ class BaseApp:
self.log = logging.getLogger(self.log_name)
self.log.propagate = False
# Remove any existing handlers
self.log.handlers = []
formatter = logging.Formatter('%(asctime)s %(levelname)s : %(message)s')
stream_stdout = logging.StreamHandler()
if self.log_name != 'BasicSwap':
@@ -81,6 +84,12 @@ class BaseApp:
testnet_name = '' if self.chain == 'mainnet' else chainparams[coin][self.chain].get('name', self.chain)
return os.path.join(datadir, testnet_name)
def getCoinIdFromName(self, coin_name):
for c, params in chainparams.items():
if coin_name.lower() == params['name'].lower():
return c
raise ValueError('Unknown coin: {}'.format(coin_name))
def getTicker(self, coin_type):
ticker = chainparams[coin_type]['ticker']
if self.chain == 'testnet':

View File

@@ -551,7 +551,6 @@ class BasicSwap(BaseApp):
self.coin_clients[coin]['walletrpcauth'] = (chain_client_settings['walletrpcuser'], chain_client_settings['walletrpcpassword'])
else:
raise ValueError('Missing XMR wallet rpc credentials.')
self.coin_clients[coin]['interface'] = self.createInterface(coin)
def ci(self, coin): # coin interface
return self.coin_clients[coin]['interface']
@@ -593,17 +592,17 @@ class BasicSwap(BaseApp):
try:
with open(pidfilepath, 'rb') as fp:
datadir_pid = int(fp.read().decode('utf-8'))
assert(datadir_pid == cc['pid'])
assert(datadir_pid == cc['pid']), 'Mismatched pid'
assert(os.path.exists(authcookiepath))
except Exception:
time.sleep(0.5)
try:
if os.name != 'nt' or cc['core_version_group'] > 17: # litecoin on windows doesn't write a pid file
assert(datadir_pid == cc['pid'])
assert(datadir_pid == cc['pid']), 'Mismatched pid'
with open(authcookiepath, 'rb') as fp:
cc['rpcauth'] = fp.read().decode('utf-8')
except Exception:
self.log.error('Unable to read authcookie for %s, %s, datadir pid %d, daemon pid %s', str(coin), authcookiepath, datadir_pid, cc['pid'])
except Exception as e:
self.log.error('Unable to read authcookie for %s, %s, datadir pid %d, daemon pid %s. Error: %s', str(coin), authcookiepath, datadir_pid, cc['pid'], str(e))
raise ValueError('Error, terminating')
def createCoinInterface(self, coin):
@@ -626,6 +625,9 @@ class BasicSwap(BaseApp):
self.log.info('%s Core version %d', chainparams[c]['name'].capitalize(), core_version)
self.coin_clients[c]['core_version'] = core_version
if c == Coins.XMR:
self.coin_clients[c]['interface'].ensureWalletExists()
if c == Coins.PART:
self.coin_clients[c]['have_spent_index'] = self.coin_clients[c]['interface'].haveSpentIndex()
@@ -636,6 +638,8 @@ class BasicSwap(BaseApp):
self.initialise()
def stopDaemon(self, coin):
if coin == Coins.XMR:
return
num_tries = 10
authcookiepath = os.path.join(self.getChainDatadirPath(coin), '.cookie')
stopping = False
@@ -697,6 +701,19 @@ class BasicSwap(BaseApp):
if synced < 1.0:
raise ValueError('{} chain is still syncing, currently at {}.'.format(self.coin_clients[c]['name'], synced))
def initialiseWallet(self, coin_type):
ci = self.ci(coin_type)
self.log.info('Initialising {} wallet.'.format(ci.coin_name()))
if coin_type == Coins.XMR:
key_view = self.getWalletKey(coin_type, 1, for_ed25519=True)
key_spend = self.getWalletKey(coin_type, 2, for_ed25519=True)
ci.initialiseWallet(key_view, key_spend)
return
root_key = self.getWalletKey(coin_type, 1)
ci.initialiseWallet(root_key)
def setIntKV(self, str_key, int_val):
session = scoped_session(self.session_factory)
kv = session.query(DBKVInt).filter_by(key=str_key).first()
@@ -963,19 +980,8 @@ class BasicSwap(BaseApp):
session.remove()
self.mxDB.release()
def getPathKey(self, coin_from, coin_to, offer_created_at, contract_count, key_no, for_xmr=False):
account = self.callcoinrpc(Coins.PART, 'extkey', ['account'])
evkey = self.callcoinrpc(Coins.PART, 'extkey', ['account', 'default', 'true'])['evkey']
ci = self.ci(coin_to)
days = offer_created_at // 86400
secs = offer_created_at - days * 86400
key_path_base = '44445555h/999999/{}/{}/{}/{}/{}/{}'.format(int(coin_from), int(coin_to), days, secs, contract_count, key_no)
if not for_xmr:
extkey = self.callcoinrpc(Coins.PART, 'extkey', ['info', evkey, key_path_base])['key_info']['result']
return decodeWif(self.callcoinrpc(Coins.PART, 'extkey', ['info', extkey])['key_info']['privkey'])
def grindForEd25519Key(self, coin_type, evkey, key_path_base):
ci = self.ci(coin_type)
nonce = 1
while True:
key_path = key_path_base + '/{}'.format(nonce)
@@ -986,7 +992,34 @@ class BasicSwap(BaseApp):
return privkey
nonce += 1
if nonce > 1000:
raise ValueError('getKeyForXMR failed')
raise ValueError('grindForEd25519Key failed')
def getWalletKey(self, coin_type, key_num, for_ed25519=False):
account = self.callcoinrpc(Coins.PART, 'extkey', ['account'])
evkey = self.callcoinrpc(Coins.PART, 'extkey', ['account', 'default', 'true'])['evkey']
key_path_base = '44445555h/1h/{}/{}'.format(int(coin_type), key_num)
if not for_ed25519:
extkey = self.callcoinrpc(Coins.PART, 'extkey', ['info', evkey, key_path_base])['key_info']['result']
return decodeWif(self.callcoinrpc(Coins.PART, 'extkey', ['info', extkey])['key_info']['privkey'])
return self.grindForEd25519Key(coin_type, evkey, key_path_base)
def getPathKey(self, coin_from, coin_to, offer_created_at, contract_count, key_no, for_ed25519=False):
account = self.callcoinrpc(Coins.PART, 'extkey', ['account'])
evkey = self.callcoinrpc(Coins.PART, 'extkey', ['account', 'default', 'true'])['evkey']
ci = self.ci(coin_to)
days = offer_created_at // 86400
secs = offer_created_at - days * 86400
key_path_base = '44445555h/999999/{}/{}/{}/{}/{}/{}'.format(int(coin_from), int(coin_to), days, secs, contract_count, key_no)
if not for_ed25519:
extkey = self.callcoinrpc(Coins.PART, 'extkey', ['info', evkey, key_path_base])['key_info']['result']
return decodeWif(self.callcoinrpc(Coins.PART, 'extkey', ['info', extkey])['key_info']['privkey'])
return self.grindForEd25519Key(coin_to, evkey, key_path_base)
def getContractPubkey(self, date, contract_count):
account = self.callcoinrpc(Coins.PART, 'extkey', ['account'])
@@ -1550,8 +1583,8 @@ class BasicSwap(BaseApp):
xmr_swap.contract_count = self.getNewContractId()
xmr_swap.dest_af = msg_buf.dest_af
xmr_swap.b_restore_height = self.ci(coin_to).getChainHeight()
kbvf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 1, for_xmr=True)
kbsf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 2, for_xmr=True)
kbvf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 1, for_ed25519=True)
kbsf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 2, for_ed25519=True)
kaf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 3)
karf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 4)
@@ -1660,8 +1693,8 @@ class BasicSwap(BaseApp):
contract_secret = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 1)
kbvl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_xmr=True)
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_xmr=True)
kbvl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519=True)
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_ed25519=True)
kal = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 4)
karl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 5)
@@ -3746,7 +3779,7 @@ class BasicSwap(BaseApp):
ci_from = self.ci(coin_from)
ci_to = self.ci(coin_to)
kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_xmr=True)
kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519=True)
kaf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3)
al_lock_spend_sig = ci_from.decryptOtVES(kbsf, xmr_swap.al_lock_spend_tx_esig)
@@ -3801,7 +3834,7 @@ class BasicSwap(BaseApp):
kbsf = ci_from.recoverEncKey(xmr_swap.al_lock_spend_tx_esig, xmr_swap.al_lock_spend_tx_sig, xmr_swap.pkasf)
assert(kbsf is not None)
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_xmr=True)
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_ed25519=True)
vkbs = ci_to.sumKeys(kbsl, kbsf)
address_to = ci_to.getMainWalletAddress()
@@ -3835,7 +3868,7 @@ class BasicSwap(BaseApp):
kbsl = ci_from.recoverEncKey(xmr_swap.af_lock_refund_spend_tx_esig, af_lock_refund_spend_tx_sig, xmr_swap.pkasl)
assert(kbsl is not None)
kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_xmr=True)
kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519=True)
vkbs = ci_to.sumKeys(kbsl, kbsf)
address_to = ci_to.getMainWalletAddress()
@@ -3874,7 +3907,7 @@ class BasicSwap(BaseApp):
xmr_swap.af_lock_refund_spend_tx_esig = msg_data.af_lock_refund_spend_tx_esig
xmr_swap.af_lock_refund_tx_sig = msg_data.af_lock_refund_tx_sig
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_xmr=True)
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_ed25519=True)
karl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 5)
xmr_swap.af_lock_refund_spend_tx_sig = ci_from.decryptOtVES(kbsl, xmr_swap.af_lock_refund_spend_tx_esig)

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019 tecnovert
# Copyright (c) 2019-2020 tecnovert
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
@@ -70,6 +70,7 @@ chainparams = {
'rpcport': 8332,
'pubkey_address': 0,
'script_address': 5,
'key_prefix': 128,
'hrp': 'bc',
'bip44': 0,
'min_amount': 1000,
@@ -79,6 +80,7 @@ chainparams = {
'rpcport': 18332,
'pubkey_address': 111,
'script_address': 196,
'key_prefix': 239,
'hrp': 'tb',
'bip44': 1,
'min_amount': 1000,
@@ -89,6 +91,7 @@ chainparams = {
'rpcport': 18443,
'pubkey_address': 111,
'script_address': 196,
'key_prefix': 239,
'hrp': 'bcrt',
'bip44': 1,
'min_amount': 1000,
@@ -105,6 +108,7 @@ chainparams = {
'rpcport': 9332,
'pubkey_address': 48,
'script_address': 50,
'key_prefix': 176,
'hrp': 'ltc',
'bip44': 2,
'min_amount': 1000,
@@ -114,6 +118,7 @@ chainparams = {
'rpcport': 19332,
'pubkey_address': 111,
'script_address': 58,
'key_prefix': 239,
'hrp': 'tltc',
'bip44': 1,
'min_amount': 1000,
@@ -124,6 +129,7 @@ chainparams = {
'rpcport': 19443,
'pubkey_address': 111,
'script_address': 58,
'key_prefix': 239,
'hrp': 'rltc',
'bip44': 1,
'min_amount': 1000,

View File

@@ -8,6 +8,7 @@ import os
CONFIG_FILENAME = 'basicswap.json'
DEFAULT_DATADIR = '~/.basicswap'
DEFAULT_ALLOW_CORS = False
TEST_DATADIRS = os.path.expanduser(os.getenv('DATADIRS', '/tmp/basicswap'))
bin_suffix = ('.exe' if os.name == 'nt' else '')

View File

@@ -17,6 +17,7 @@ from .util import (
dumpj,
format_amount,
make_int,
toWIF,
decodeAddress)
from coincurve.keys import (
PublicKey)
@@ -131,6 +132,16 @@ class BTCInterface(CoinInterface):
def getBlockchainInfo(self):
return self.rpc_callback('getblockchaininfo')
def initialiseWallet(self, key_bytes):
wif_prefix = chainparams[self.coin_type()][self._network]['key_prefix']
key_wif = toWIF(wif_prefix, key_bytes)
try:
self.rpc_callback('sethdseed', [True, key_wif])
except Exception as e:
# < 0.21: Cannot set a new HD seed while still in Initial Block Download.
logging.error('sethdseed failed: {}'.format(str(e)))
def getWalletInfo(self):
return self.rpc_callback('getwalletinfo')

View File

@@ -40,3 +40,6 @@ class PARTInterface(BTCInterface):
version = self.getDaemonVersion()
index_info = self.rpc_callback('getinsightinfo' if int(str(version)[:2]) > 19 else 'getindexinfo')
return index_info['spentindex']
def initialiseWallet(self, key):
raise ValueError('TODO')

View File

@@ -65,11 +65,45 @@ class XMRInterface(CoinInterface):
def setWalletFilename(self, wallet_filename):
self._wallet_filename = wallet_filename
def initialiseWallet(self, key_view, key_spend, restore_height=None):
try:
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
# TODO: Check address
return # Wallet exists
except Exception as e:
pass
try:
if restore_height is None:
restore_height = self.getChainHeight()
except Exception as e:
logging.warning('Unable to get restore_height, set to zero. Error: {}'.format(str(e)))
restore_height = 0
Kbv = self.getPubkey(key_view)
Kbs = self.getPubkey(key_spend)
address_b58 = xmr_util.encode_address(Kbv, Kbs)
params = {
'filename': self._wallet_filename,
'address': address_b58,
'viewkey': b2h(key_view[::-1]),
'spendkey': b2h(key_spend[::-1]),
'restore_height': restore_height,
}
rv = self.rpc_wallet_cb('generate_from_keys', params)
logging.info('generate_from_keys %s', dumpj(rv))
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
def ensureWalletExists(self):
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
def testDaemonRPC(self):
self.rpc_wallet_cb('get_languages')
def getDaemonVersion(self):
return self.rpc_cb('get_version')['version']
return self.rpc_wallet_cb('get_version')['version']
#return self.rpc_cb('get_version')['version']
def getBlockchainInfo(self):
rv = {}

View File

@@ -15,7 +15,7 @@ class Peer:
class Network:
def __init__(self, network_port, network_key):
self._network_port = network_port
def __init__(self, p2p_port, network_key):
self._p2p_port = p2p_port
self._network_key = network_key
self._peers = []