def getNewAddress(account_id,wallet_id): try: wallet = HDWallet(wallet_id, db_uri=db_uri) address = wallet.new_key(account_id=account_id) address = Address(address_id=address.address, network_name=address.network_name) return address except Exception as e: raise ServiceException(get_env_var('exception.business.newaddress.dumpkey.serviceexception'))
class BitcoinWallet(Wallet): """ This class is responsible for handling your wallet of bitcoins. NOTE: all imports of bitcoinlib should be local. The reason for this is that we are patching the bitcoinlib_main method in the __init__ method of the class (since we need access to the Tribler state directory) and we can only import bitcoinlib *after* patching the bitcoinlib main file. """ TESTNET = False def __init__(self, wallet_dir): super(BitcoinWallet, self).__init__() bitcoinlib_main.initialize_lib(wallet_dir) from bitcoinlib.wallets import wallet_exists, HDWallet self.network = 'testnet' if self.TESTNET else 'bitcoin' self.wallet_dir = wallet_dir self.min_confirmations = 0 self.wallet = None self.unlocked = True self.db_path = os.path.join(wallet_dir, 'wallets.sqlite') self.wallet_name = 'tribler_testnet' if self.TESTNET else 'tribler' if wallet_exists(self.wallet_name, databasefile=self.db_path): self.wallet = HDWallet(self.wallet_name, databasefile=self.db_path) self.created = True def get_name(self): return 'Bitcoin' def get_identifier(self): return 'BTC' def create_wallet(self): """ Create a new bitcoin wallet. """ from bitcoinlib.wallets import wallet_exists, HDWallet, WalletError if wallet_exists(self.wallet_name, databasefile=self.db_path): return fail( RuntimeError( f"Bitcoin wallet with name {self.wallet_name} already exists." )) self._logger.info("Creating wallet in %s", self.wallet_dir) try: self.wallet = HDWallet.create(self.wallet_name, network=self.network, databasefile=self.db_path) self.wallet.new_key('tribler_payments') self.wallet.new_key('tribler_change', change=1) self.created = True except WalletError as exc: self._logger.error("Cannot create BTC wallet!") return fail(exc) return succeed(None) def get_balance(self): """ Return the balance of the wallet. """ if self.created: self.wallet.utxos_update(networks=self.network) return succeed({ "available": self.wallet.balance(network=self.network), "pending": 0, "currency": 'BTC', "precision": self.precision() }) return succeed({ "available": 0, "pending": 0, "currency": 'BTC', "precision": self.precision() }) async def transfer(self, amount, address): balance = await self.get_balance() if balance['available'] >= int(amount): self._logger.info( "Creating Bitcoin payment with amount %f to address %s", amount, address) tx = self.wallet.send_to(address, int(amount)) return str(tx.hash) raise InsufficientFunds("Insufficient funds") def monitor_transaction(self, txid): """ Monitor a given transaction ID. Returns a Deferred that fires when the transaction is present. """ monitor_future = Future() async def monitor(): transactions = await self.get_transactions() for transaction in transactions: if transaction['id'] == txid: self._logger.debug("Found transaction with id %s", txid) monitor_future.set_result(None) monitor_task.cancel() self._logger.debug("Start polling for transaction %s", txid) monitor_task = self.register_task(f"btc_poll_{txid}", monitor, interval=5) return monitor_future def get_address(self): if not self.created: return '' return self.wallet.keys(name='tribler_payments', is_active=False)[0].address def get_transactions(self): if not self.created: return succeed([]) from bitcoinlib.transactions import Transaction from bitcoinlib.wallets import DbTransaction, DbTransactionInput # Update all transactions self.wallet.transactions_update(network=self.network) txs = self.wallet._session.query(DbTransaction.raw, DbTransaction.confirmations, DbTransaction.date, DbTransaction.fee)\ .filter(DbTransaction.wallet_id == self.wallet.wallet_id)\ .all() transactions = [] for db_result in txs: transaction = Transaction.import_raw(db_result[0], network=self.network) transaction.confirmations = db_result[1] transaction.date = db_result[2] transaction.fee = db_result[3] transactions.append(transaction) # Sort them based on locktime transactions.sort(key=lambda tx: tx.locktime, reverse=True) my_keys = [ key.address for key in self.wallet.keys(network=self.network, is_active=False) ] transactions_list = [] for transaction in transactions: value = 0 input_addresses = [] output_addresses = [] for tx_input in transaction.inputs: input_addresses.append(tx_input.address) if tx_input.address in my_keys: # At this point, we do not have the value of the input so we should do a database query for it db_res = self.wallet._session.query( DbTransactionInput.value).filter( hexlify(tx_input.prev_hash) == DbTransactionInput.prev_hash, tx_input.output_n_int == DbTransactionInput.output_n).all() if db_res: value -= db_res[0][0] for tx_output in transaction.outputs: output_addresses.append(tx_output.address) if tx_output.address in my_keys: value += tx_output.value transactions_list.append({ 'id': transaction.hash, 'outgoing': value < 0, 'from': ','.join(input_addresses), 'to': ','.join(output_addresses), 'amount': abs(value), 'fee_amount': transaction.fee, 'currency': 'BTC', 'timestamp': time.mktime(transaction.date.timetuple()), 'description': f'Confirmations: {transaction.confirmations}' }) return succeed(transactions_list) def min_unit(self): return 100000 # The minimum amount of BTC we can transfer in this market is 1 mBTC (100000 Satoshi) def precision(self): return 8
class BitcoinWallet(Wallet): """ This class is responsible for handling your wallet of bitcoins. NOTE: all imports of bitcoinlib should be local. The reason for this is that we are patching the bitcoinlib_main method in the __init__ method of the class (since we need access to the Tribler state directory) and we can only import bitcoinlib *after* patching the bitcoinlib main file. """ TESTNET = False def __init__(self, wallet_dir): super(BitcoinWallet, self).__init__() bitcoinlib_main.initialize_lib(wallet_dir) from bitcoinlib.wallets import wallet_exists, HDWallet self.network = 'testnet' if self.TESTNET else 'bitcoin' self.wallet_dir = wallet_dir self.min_confirmations = 0 self.wallet = None self.unlocked = True self.db_path = os.path.join(wallet_dir, 'wallets.sqlite') self.wallet_name = 'tribler_testnet' if self.TESTNET else 'tribler' if wallet_exists(self.wallet_name, databasefile=self.db_path): self.wallet = HDWallet(self.wallet_name, databasefile=self.db_path) self.created = True def get_name(self): return 'Bitcoin' def get_identifier(self): return 'BTC' def create_wallet(self): """ Create a new bitcoin wallet. """ from bitcoinlib.wallets import wallet_exists, HDWallet, WalletError if wallet_exists(self.wallet_name, databasefile=self.db_path): return fail(RuntimeError("Bitcoin wallet with name %s already exists." % self.wallet_name)) self._logger.info("Creating wallet in %s", self.wallet_dir) try: self.wallet = HDWallet.create(self.wallet_name, network=self.network, databasefile=self.db_path) self.wallet.new_key('tribler_payments') self.wallet.new_key('tribler_change', change=1) self.created = True except WalletError as exc: self._logger.error("Cannot create BTC wallet!") return fail(Failure(exc)) return succeed(None) def get_balance(self): """ Return the balance of the wallet. """ if self.created: self.wallet.utxos_update(networks=self.network) return succeed({ "available": self.wallet.balance(network=self.network), "pending": 0, "currency": 'BTC', "precision": self.precision() }) return succeed({"available": 0, "pending": 0, "currency": 'BTC', "precision": self.precision()}) def transfer(self, amount, address): def on_balance(balance): if balance['available'] >= int(amount): self._logger.info("Creating Bitcoin payment with amount %f to address %s", amount, address) tx = self.wallet.send_to(address, int(amount)) return str(tx.hash) else: return fail(InsufficientFunds("Insufficient funds")) return self.get_balance().addCallback(on_balance) def monitor_transaction(self, txid): """ Monitor a given transaction ID. Returns a Deferred that fires when the transaction is present. """ monitor_deferred = Deferred() @inlineCallbacks def monitor_loop(): transactions = yield self.get_transactions() for transaction in transactions: if transaction['id'] == txid: self._logger.debug("Found transaction with id %s", txid) monitor_deferred.callback(None) monitor_lc.stop() self._logger.debug("Start polling for transaction %s", txid) monitor_lc = self.register_task("btc_poll_%s" % txid, LoopingCall(monitor_loop)) monitor_lc.start(5) return monitor_deferred def get_address(self): if not self.created: return '' return self.wallet.keys(name='tribler_payments', is_active=False)[0].address def get_transactions(self): if not self.created: return succeed([]) from bitcoinlib.transactions import Transaction from bitcoinlib.wallets import DbTransaction, DbTransactionInput # Update all transactions self.wallet.transactions_update(network=self.network) txs = self.wallet._session.query(DbTransaction.raw, DbTransaction.confirmations, DbTransaction.date, DbTransaction.fee)\ .filter(DbTransaction.wallet_id == self.wallet.wallet_id)\ .all() transactions = [] for db_result in txs: transaction = Transaction.import_raw(db_result[0], network=self.network) transaction.confirmations = db_result[1] transaction.date = db_result[2] transaction.fee = db_result[3] transactions.append(transaction) # Sort them based on locktime transactions.sort(key=lambda tx: tx.locktime, reverse=True) my_keys = [key.address for key in self.wallet.keys(network=self.network, is_active=False)] transactions_list = [] for transaction in transactions: value = 0 input_addresses = [] output_addresses = [] for tx_input in transaction.inputs: input_addresses.append(tx_input.address) if tx_input.address in my_keys: # At this point, we do not have the value of the input so we should do a database query for it db_res = self.wallet._session.query(DbTransactionInput.value).filter( hexlify(tx_input.prev_hash) == DbTransactionInput.prev_hash, tx_input.output_n_int == DbTransactionInput.output_n).all() if db_res: value -= db_res[0][0] for tx_output in transaction.outputs: output_addresses.append(tx_output.address) if tx_output.address in my_keys: value += tx_output.value transactions_list.append({ 'id': transaction.hash, 'outgoing': value < 0, 'from': ','.join(input_addresses), 'to': ','.join(output_addresses), 'amount': abs(value), 'fee_amount': transaction.fee, 'currency': 'BTC', 'timestamp': time.mktime(transaction.date.timetuple()), 'description': 'Confirmations: %d' % transaction.confirmations }) return succeed(transactions_list) def min_unit(self): return 100000 # The minimum amount of BTC we can transfer in this market is 1 mBTC (100000 Satoshi) def precision(self): return 8
class BitcoinWallet(Wallet): """ This class is responsible for handling your wallet of bitcoins. """ TESTNET = False def __init__(self, wallet_dir): super(BitcoinWallet, self).__init__() self.network = 'testnet' if self.TESTNET else 'bitcoin' self.wallet_dir = wallet_dir self.min_confirmations = 0 self.wallet = None self.unlocked = True self.db_path = os.path.join(wallet_dir, 'wallets.sqlite') self.wallet_name = 'tribler_testnet' if self.TESTNET else 'tribler' if wallet_exists(self.wallet_name, databasefile=self.db_path): self.wallet = HDWallet(self.wallet_name, databasefile=self.db_path) self.created = True def get_name(self): return 'Bitcoin' def get_identifier(self): return 'BTC' def create_wallet(self): """ Create a new bitcoin wallet. """ self._logger.info("Creating wallet in %s", self.wallet_dir) try: self.wallet = HDWallet.create(self.wallet_name, network=self.network, databasefile=self.db_path) self.wallet.new_key('tribler_payments') self.wallet.new_key('tribler_change', change=1) self.created = True except WalletError as exc: self._logger.error("Cannot create BTC wallet!") return fail(Failure(exc)) return succeed(None) def get_balance(self): """ Return the balance of the wallet. """ if self.created: self.wallet.utxos_update(networks=self.network) return succeed({ "available": self.wallet.balance(network=self.network), "pending": 0, "currency": 'BTC', "precision": self.precision() }) return succeed({"available": 0, "pending": 0, "currency": 'BTC', "precision": self.precision()}) def transfer(self, amount, address): def on_balance(balance): if balance['available'] >= amount: self._logger.info("Creating Bitcoin payment with amount %f to address %s", amount, address) tx = self.wallet.send_to(address, int(amount)) return str(tx.hash) else: return fail(InsufficientFunds()) return self.get_balance().addCallback(on_balance) def monitor_transaction(self, txid): """ Monitor a given transaction ID. Returns a Deferred that fires when the transaction is present. """ monitor_deferred = Deferred() @inlineCallbacks def monitor_loop(): transactions = yield self.get_transactions() for transaction in transactions: if transaction['id'] == txid: self._logger.debug("Found transaction with id %s", txid) monitor_deferred.callback(None) monitor_lc.stop() self._logger.debug("Start polling for transaction %s", txid) monitor_lc = self.register_task("btc_poll_%s" % txid, LoopingCall(monitor_loop)) monitor_lc.start(5) return monitor_deferred def get_address(self): if not self.created: return '' return self.wallet.keys(name='tribler_payments', is_active=False)[0].address def get_transactions(self): if not self.created: return succeed([]) # Update all transactions self.wallet.transactions_update(network=self.network) txs = self.wallet._session.query(DbTransaction.raw, DbTransaction.confirmations, DbTransaction.date, DbTransaction.fee)\ .filter(DbTransaction.wallet_id == self.wallet.wallet_id)\ .all() transactions = [] for db_result in txs: transaction = Transaction.import_raw(db_result[0], network=self.network) transaction.confirmations = db_result[1] transaction.date = db_result[2] transaction.fee = db_result[3] transactions.append(transaction) # Sort them based on locktime transactions.sort(key=lambda tx: tx.locktime, reverse=True) my_keys = [key.address for key in self.wallet.keys(network=self.network, is_active=False)] transactions_list = [] for transaction in transactions: value = 0 input_addresses = [] output_addresses = [] for tx_input in transaction.inputs: input_addresses.append(tx_input.address) if tx_input.address in my_keys: # At this point, we do not have the value of the input so we should do a database query for it db_res = self.wallet._session.query(DbTransactionInput.value).filter( tx_input.prev_hash.encode('hex') == DbTransactionInput.prev_hash, tx_input.output_n_int == DbTransactionInput.output_n).all() if db_res: value -= db_res[0][0] for tx_output in transaction.outputs: output_addresses.append(tx_output.address) if tx_output.address in my_keys: value += tx_output.value transactions_list.append({ 'id': transaction.hash, 'outgoing': value < 0, 'from': ','.join(input_addresses), 'to': ','.join(output_addresses), 'amount': abs(value), 'fee_amount': transaction.fee, 'currency': 'BTC', 'timestamp': time.mktime(transaction.date.timetuple()), 'description': 'Confirmations: %d' % transaction.confirmations }) return succeed(transactions_list) def min_unit(self): return 100000 # The minimum amount of BTC we can transfer in this market is 1 mBTC (100000 Satoshi) def precision(self): return 8
w = HDWallet.create( name='Wallet Error', db_uri=test_database) try: w.import_key(key='T43gB4F6k1Ly3YWbMuddq13xLb56hevUDP3RthKArr7FPHjQiXpp', network='litecoin') except WalletError as e: print("Import litecoin key in bitcoin wallet gives an EXPECTED error: %s" % e) print("\n=== Normalize BIP32 key path ===") key_path = "m/44h/1'/0p/2000/1" print("Raw: %s, Normalized: %s" % (key_path, normalize_path(key_path))) print("\n=== Send test bitcoins to an address ===") wallet_import = HDWallet('TestNetWallet', db_uri=test_database) for _ in range(10): wallet_import.new_key() wallet_import.utxos_update(99) wallet_import.info() utxos = wallet_import.utxos(99) try: res = wallet_import.send_to('mxdLD8SAGS9fe2EeCXALDHcdTTbppMHp8N', 1000, 99) print("Send transaction result:") if res.hash: print("Successfully send, tx id:", res.hash) else: print("TX not send, result:", res.errors) except WalletError as e: print("TX not send, error: %s" % e.msg) except Exception as e: print(e)
class BitcoinlibWallet(Wallet): """ Superclass used for the implementation of bitcoinlib wallets. """ def __init__(self, wallet_dir, testnet, network, currency): if network not in SUPPORTED_NETWORKS: raise UnsupportedNetwork(network) super(BitcoinlibWallet, self).__init__() self.network = network self.wallet_name = f'tribler_{self.network}' self.testnet = testnet self.unlocked = True self.currency = currency self.wallet_dir = wallet_dir self.min_confirmations = 0 self.wallet = None self.db_path = os.path.join(wallet_dir, 'wallets.sqlite') if wallet_exists(self.wallet_name, db_uri=self.db_path): self.wallet = HDWallet(self.wallet_name, db_uri=self.db_path) self.created = True self.lib_init() def cfg_init(self): """ Adjusts the bitcoinlib configuration for the creation of a wallet. """ config = ConfigParser() config['locations'] = {} locations = config['locations'] locations['data_dir'] = self.wallet_dir.__str__() # locations['database_dir'] = 'database' # locations['default_databasefile'] = 'bitcoinlib.sqlite' # locations['default_databasefile_cache'] = 'bitcoinlib_cache.sqlite' locations['log_file'] = self.network + '_log.log' config['common'] = {} common = config['common'] # common['allow_database_threads'] = 'True' # common['timeout_requests'] = '5' # common['default_language'] = 'english' common['default_network'] = self.network # common['default_witness_type'] = '' # common['service_caching_enabled'] = 'True' config['logs'] = {} logs = config['logs'] logs['enable_bitcoinlib_logging'] = 'False' logs['loglevel'] = 'INFO' return config def lib_init(self): """ Initializes bitcoinlib by creating a configuration file and setting the environmental variable. """ cfg_name = 'bcl_config.ini' config = self.cfg_init() with open(cfg_name, 'w') as configfile: config.write(configfile) os.environ['BCL_CONFIG_FILE'] = os.path.abspath(cfg_name) def get_identifier(self): return self.currency def get_name(self): return self.network def create_wallet(self): if self.created: return fail(RuntimeError(f"{self.network} wallet with name {self.wallet_name} already exists.")) self._logger.info("Creating wallet in %s", self.wallet_dir) try: self.wallet = HDWallet.create(self.wallet_name, network=self.network, db_uri=self.db_path) self.wallet.new_key('tribler_payments') self.wallet.new_key('tribler_change', change=1) self.created = True except WalletError as exc: self._logger.error("Cannot create %s wallet!", self.network) return fail(exc) return succeed(None) def get_balance(self): if not self.created: return succeed({ "available": 0, "pending": 0, "currency": self.currency, "precision": self.precision() }) self.wallet.utxos_update(networks=self.network) return succeed({ "available": self.wallet.balance(network=self.network), "pending": 0, "currency": self.currency, "precision": self.precision() }) async def transfer(self, amount, address): balance = await self.get_balance() if balance['available'] >= int(amount): self._logger.info("Creating %s payment with amount %d to address %s", self.network, int(amount), address) tx = self.wallet.send_to(address, int(amount)) return str(tx.hash) raise InsufficientFunds("Insufficient funds") def get_address(self): if not self.created: return succeed('') return succeed(self.wallet.keys(name='tribler_payments', is_active=False)[-1].address) def get_transactions(self): if not self.created: return succeed([]) # Update all transactions self.wallet.transactions_update(network=self.network) # TODO: 'Access to a protected member _session of a class' txs = self.wallet._session.query(DbTransaction.raw, DbTransaction.confirmations, DbTransaction.date, DbTransaction.fee) \ .filter(DbTransaction.wallet_id == self.wallet.wallet_id) \ .all() transactions = [] for db_result in txs: transaction = Transaction.import_raw(db_result[0], network=self.network) transaction.confirmations = db_result[1] transaction.date = db_result[2] transaction.fee = db_result[3] transactions.append(transaction) # Sort them based on locktime transactions.sort(key=lambda tx: tx.locktime, reverse=True) my_keys = [key.address for key in self.wallet.keys(network=self.network, is_active=False)] transactions_list = [] for transaction in transactions: value = 0 input_addresses = [] output_addresses = [] for tx_input in transaction.inputs: input_addresses.append(tx_input.address) if tx_input.address in my_keys: # At this point, we do not have the value of the input so we should do a database query for it db_res = self.wallet._session.query(DbTransactionInput.value).filter( hexlify(tx_input.prev_hash) == DbTransactionInput.prev_hash, tx_input.output_n_int == DbTransactionInput.output_n).all() if db_res: value -= db_res[0][0] # TODO: db_res[0][0] not an int, but hash string (value/fee expected?) for tx_output in transaction.outputs: output_addresses.append(tx_output.address) if tx_output.address in my_keys: value += tx_output.value transactions_list.append({ 'id': transaction.hash, 'outgoing': value < 0, 'from': ','.join(input_addresses), 'to': ','.join(output_addresses), 'amount': abs(value), 'fee_amount': transaction.fee, 'currency': self.currency, 'timestamp': time.mktime(transaction.date.timetuple()), 'description': f'Confirmations: {transaction.confirmations}' }) return succeed(transactions_list) def min_unit(self): return 100000 # For LTC, BTC and DASH, the minimum trade should be 100.000 basic units (Satoshi, duffs) def precision(self): return 8 # The precision for LTC, BTC and DASH is the same. def is_testnet(self): return self.testnet