diff --git a/electrum/plugins/trustedcoin/qt.py b/electrum/plugins/trustedcoin/qt.py index da4ce84cd..c84a2bbb0 100644 --- a/electrum/plugins/trustedcoin/qt.py +++ b/electrum/plugins/trustedcoin/qt.py @@ -195,18 +195,6 @@ class Plugin(TrustedCoinPlugin): vbox.addLayout(Buttons(CloseButton(d))) d.exec_() - def on_buy(self, window, k, v, d): - d.close() - if window.pluginsdialog: - window.pluginsdialog.close() - wallet = window.wallet - uri = "bitcoin:" + wallet.billing_info['billing_address'] + "?message=TrustedCoin %d Prepaid Transactions&amount="%k + str(Decimal(v)/100000000) - wallet.is_billing = True - window.pay_to_URI(uri) - window.payto_e.setFrozen(True) - window.message_e.setFrozen(True) - window.amount_e.setFrozen(True) - def go_online_dialog(self, wizard): msg = [ _("Your wallet file is: {}.").format(os.path.abspath(wizard.storage.path)), diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py index adf3f413e..d93bf3541 100644 --- a/electrum/plugins/trustedcoin/trustedcoin.py +++ b/electrum/plugins/trustedcoin/trustedcoin.py @@ -28,13 +28,15 @@ import json import base64 import time import hashlib +from collections import defaultdict +from typing import Dict from urllib.parse import urljoin from urllib.parse import quote from aiohttp import ClientResponse -from electrum import ecc, constants, keystore, version, bip32 -from electrum.bitcoin import TYPE_ADDRESS, is_new_seed, public_key_to_p2pkh, seed_type, is_any_2fa_seed_type +from electrum import ecc, constants, keystore, version, bip32, bitcoin +from electrum.bitcoin import TYPE_ADDRESS, is_new_seed, seed_type, is_any_2fa_seed_type from electrum.bip32 import (deserialize_xpub, deserialize_xprv, bip32_private_key, CKD_pub, serialize_xpub, bip32_root, bip32_private_derivation, xpub_type) from electrum.crypto import sha256 @@ -244,14 +246,18 @@ class Wallet_2fa(Multisig_Wallet): self.is_billing = False self.billing_info = None self._load_billing_addresses() + self.plugin = None # type: TrustedCoinPlugin def _load_billing_addresses(self): billing_addresses = self.storage.get('trustedcoin_billing_addresses', {}) - self._billing_addresses = {} # index -> addr - # convert keys from str to int - for index, addr in list(billing_addresses.items()): - self._billing_addresses[int(index)] = addr - self._billing_addresses_set = set(self._billing_addresses.values()) # set of addrs + self._billing_addresses = defaultdict(dict) # type: Dict[str, Dict[int, str]] # addr_type -> index -> addr + self._billing_addresses_set = set() # set of addrs + for addr_type, d in list(billing_addresses.items()): + self._billing_addresses[addr_type] = {} + # convert keys from str to int + for index, addr in d.items(): + self._billing_addresses[addr_type][int(index)] = addr + self._billing_addresses_set.add(addr) def can_sign_without_server(self): return not self.keystores['x2/'].is_watching_only() @@ -291,7 +297,7 @@ class Wallet_2fa(Multisig_Wallet): self, coins, o, config, fixed_fee, change_addr) fee = self.extra_fee(config) if not is_sweep else 0 if fee: - address = self.billing_info['billing_address'] + address = self.billing_info['billing_address_segwit'] fee_output = TxOutput(TYPE_ADDRESS, address, fee) try: tx = mk_tx(outputs + [fee_output]) @@ -322,8 +328,9 @@ class Wallet_2fa(Multisig_Wallet): self.billing_info = None self.plugin.start_request_thread(self) - def add_new_billing_address(self, billing_index: int, address: str): - saved_addr = self._billing_addresses.get(billing_index) + def add_new_billing_address(self, billing_index: int, address: str, addr_type: str): + billing_addresses_of_this_type = self._billing_addresses[addr_type] + saved_addr = billing_addresses_of_this_type.get(billing_index) if saved_addr is not None: if saved_addr == address: return # already saved this address @@ -332,15 +339,16 @@ class Wallet_2fa(Multisig_Wallet): 'for index {}, already saved {}, now got {}' .format(billing_index, saved_addr, address)) # do we have all prior indices? (are we synced?) - largest_index_we_have = max(self._billing_addresses) if self._billing_addresses else -1 + largest_index_we_have = max(billing_addresses_of_this_type) if billing_addresses_of_this_type else -1 if largest_index_we_have + 1 < billing_index: # need to sync for i in range(largest_index_we_have + 1, billing_index): - addr = make_billing_address(self, i) - self._billing_addresses[i] = addr + addr = make_billing_address(self, i, addr_type=addr_type) + billing_addresses_of_this_type[i] = addr self._billing_addresses_set.add(addr) # save this address; and persist to disk - self._billing_addresses[billing_index] = address + billing_addresses_of_this_type[billing_index] = address self._billing_addresses_set.add(address) + self._billing_addresses[addr_type] = billing_addresses_of_this_type self.storage.put('trustedcoin_billing_addresses', self._billing_addresses) # FIXME this often runs in a daemon thread, where storage.write will fail self.storage.write() @@ -365,12 +373,17 @@ def make_xpub(xpub, s): cK2, c2 = bip32._CKD_pub(cK, c, s) return serialize_xpub(version, c2, cK2) -def make_billing_address(wallet, num): +def make_billing_address(wallet, num, addr_type): long_id, short_id = wallet.get_user_id() xpub = make_xpub(get_billing_xpub(), long_id) version, _, _, _, c, cK = deserialize_xpub(xpub) cK, c = CKD_pub(cK, c, num) - return public_key_to_p2pkh(cK) + if addr_type == 'legacy': + return bitcoin.public_key_to_p2pkh(cK) + elif addr_type == 'segwit': + return bitcoin.public_key_to_p2wpkh(cK) + else: + raise ValueError(f'unexpected billing type: {addr_type}') class TrustedCoinPlugin(BasePlugin): @@ -428,7 +441,7 @@ class TrustedCoinPlugin(BasePlugin): return f @finish_requesting - def request_billing_info(self, wallet): + def request_billing_info(self, wallet: 'Wallet_2fa'): if wallet.can_sign_without_server(): return self.print_error("request billing info") @@ -438,11 +451,16 @@ class TrustedCoinPlugin(BasePlugin): self.print_error('cannot connect to TrustedCoin server: {}'.format(repr(e))) return billing_index = billing_info['billing_index'] - billing_address = make_billing_address(wallet, billing_index) - if billing_address != billing_info['billing_address']: - raise Exception('unexpected trustedcoin billing address: expected {}, received {}' - .format(billing_address, billing_info['billing_address'])) - wallet.add_new_billing_address(billing_index, billing_address) + # add segwit billing address; this will be used for actual billing + billing_address = make_billing_address(wallet, billing_index, addr_type='segwit') + if billing_address != billing_info['billing_address_segwit']: + raise Exception(f'unexpected trustedcoin billing address: ' + f'calculated {billing_address}, received {billing_info["billing_address_segwit"]}') + wallet.add_new_billing_address(billing_index, billing_address, addr_type='segwit') + # also add legacy billing address; only used for detecting past payments in GUI + billing_address = make_billing_address(wallet, billing_index, addr_type='legacy') + wallet.add_new_billing_address(billing_index, billing_address, addr_type='legacy') + wallet.billing_info = billing_info wallet.price_per_tx = dict(billing_info['price_per_tx']) wallet.price_per_tx.pop(1, None) diff --git a/electrum/storage.py b/electrum/storage.py index d526000e2..b88e83136 100644 --- a/electrum/storage.py +++ b/electrum/storage.py @@ -44,7 +44,7 @@ from .keystore import bip44_derivation OLD_SEED_VERSION = 4 # electrum versions < 2.0 NEW_SEED_VERSION = 11 # electrum versions >= 2.0 -FINAL_SEED_VERSION = 18 # electrum >= 2.7 will set this to prevent +FINAL_SEED_VERSION = 19 # electrum >= 2.7 will set this to prevent # old versions from overwriting new format @@ -354,6 +354,7 @@ class WalletStorage(JsonDB): self.convert_version_16() self.convert_version_17() self.convert_version_18() + self.convert_version_19() self.put('seed_version', FINAL_SEED_VERSION) # just to be sure self.write() @@ -572,11 +573,16 @@ class WalletStorage(JsonDB): # delete verified_tx3 as its structure changed if not self._is_upgrade_method_needed(17, 17): return - self.put('verified_tx3', None) - self.put('seed_version', 18) + def convert_version_19(self): + # delete trustedcoin_billing_addresses + if not self._is_upgrade_method_needed(18, 18): + return + self.put('trustedcoin_billing_addresses', None) + self.put('seed_version', 19) + def convert_imported(self): if not self._is_upgrade_method_needed(0, 13): return