def derive_change_address(self) -> WalletAddress: new_address = WalletAddress( self.watch_only_wallet.xpub, self.watch_only_wallet.base_keypath, len(self.watch_only_wallet.change_addresses), True) self.watch_only_wallet.change_addresses[str(new_address)] = new_address WalletFile.save(self.watch_only_wallet) return new_address
def persist_spend(self, selection: CoinSelection): for utxo in map_coin_selection_to_utxos(selection, self.watch_only_wallet): utxo.is_awaiting_spend = True if selection.change_value > 0: self.watch_only_wallet.last_change_address.is_fresh = False self.watch_only_wallet.refresh_balances() WalletFile.save(self.watch_only_wallet)
def derive_external_address(self, label) -> WalletAddress: new_address = WalletAddress( self.watch_only_wallet.xpub, self.watch_only_wallet.base_keypath, len(self.watch_only_wallet.external_addresses), label=label) self.watch_only_wallet.external_addresses[str( new_address)] = new_address WalletFile.save(self.watch_only_wallet) return new_address
def _sync_to_blockchain(self): most_recent_block = self.blockchain_client.get_most_recent_block() if most_recent_block == self.watch_only_wallet.current_block: return while self.blockchain_client.block_is_orphan( self.watch_only_wallet.current_block): self.purge_orphaned_block(self.watch_only_wallet.current_block) for address in self.watch_only_wallet.addresses: self.update_address_utxos(address) self.watch_only_wallet.current_block = most_recent_block self.watch_only_wallet.refresh_balances() WalletFile.save(self.watch_only_wallet)
def purge_orphaned_block(self, orphan: Block): for address in self.watch_only_wallet.addresses: if address.utxos: address.utxos = [ utxo for utxo in address.utxos if utxo.block.hash != orphan.hash ] if not address.utxos: address.is_fresh = self.blockchain_client.address_is_fresh( address) self.watch_only_wallet.current_block = self.blockchain_client.get_block_by_hash( self.watch_only_wallet.current_block.prev_hash) self.watch_only_wallet.refresh_balances() WalletFile.save(self.watch_only_wallet)
def _sync_to_blockchain_loop(self): bitcointx.select_chain_params(self.network) while True: try: if WalletFile.exists(): self._sync_to_blockchain() sleep(5) except ConnectionError: pass
def __init__(self, watch_only_wallet: WatchOnlyWallet): super().__init__() self.network = Config.get_network() bitcointx.select_chain_params(self.network) self.watch_only_wallet = watch_only_wallet self.blockchain_client = BlockchainClient(self.network) self.fee_estimation_client = FeeEstimationClient() self.tx_broadcast_client = TxBroadcastClient(self.network) self.serial_client = SerialClient(self.network) # Wallet is already set up if WalletFile.exists(): self.watch_only_wallet.load(*WalletFile.load()) # Wallet already got HD keys from the hardware wallet, but haven't # properly recovered balances on this side elif HardwareWalletFile.exists(): self.recover_wallet(HardwareWalletFile.load_candidate_wallets(), HardwareWalletFile.load_master_fingerprint())
def test_purge_block(mock_client): watch_only_wallet = WatchOnlyWallet() controller = MainController(watch_only_wallet) controller.blockchain_client = mock_client watch_only_wallet.load(*WalletFile.load()) controller._sync_to_blockchain() watch_only_wallet.refresh_balances() # Tx_ins from the orphaned block should not show up in our balance assert watch_only_wallet.spendable_balance_satoshis == 0 assert watch_only_wallet.incoming_balance_satoshis == 888 # Current block should be the most recent valid block assert watch_only_wallet.current_block == valid_block_4 # Addresses that received a tx_in in an orphaned block should count as fresh assert list(watch_only_wallet.external_addresses.values())[0].is_fresh
def __init__(self, main_controller, watch_only_wallet): super().__init__() self.main_controller = main_controller self.watch_only_wallet = watch_only_wallet self.init_base_ui() self.init_app_views() self.show() self.main_controller.watch_only_wallet_initialized.connect( self.handle_watch_only_wallet_initialized) if WalletFile.exists(): self.init_status_bar() self.central_widget.setCurrentIndex(self.WALLET_PAGE_INDEX) else: self.central_widget.setCurrentIndex(self.INIT_WALLET_PAGE_INDEX)
def recover_wallet(self, candidate_wallets: List[Tuple[ExtPubKey, HDKeyPath]], master_fingerprint: bytes): for wallet_xpub, base_keypath in candidate_wallets: external_addresses = self.discover_addresses( wallet_xpub, base_keypath, False) if external_addresses: break change_addresses = self.discover_addresses(wallet_xpub, base_keypath, True) self.watch_only_wallet.load(wallet_xpub, master_fingerprint, base_keypath, Block.genesis_block(), external_addresses, change_addresses) WalletFile(self.watch_only_wallet) self.watch_only_wallet_initialized.emit() if not self.watch_only_wallet.change_addresses: self.derive_change_address()