def check_inputs_for_sufficient_funds_and_return_total( self, inputs, amount): """ This function check on blockchain for sufficient funds. inputs is a dict with bitcoin pubkeys as a keys and list of utxo hashes as values. the sum of all utxo values should be greater then amount it does as follows: 1. check utxo list for every pubkey in the dict 2. check if utxo from inputs are in the utxo list on blockchain for these pubkeys 2.a return None if there is a utxo from list not in utxo set from blockchain 2.b return None if utxo in list is not confirmed 3. return True, total_amt if summary values of utxos are greater then amount and False, None otherwise returns None,None on blockchain communication error """ assert amount > 0, "Amount must be > 0!" def _utxo_name(x): return x['tx_hash'] + ":" + str(x['tx_pos']) total = 0 try: for public_key, pk_inputs in inputs.items(): try: address = Address.from_pubkey(public_key) except AddressError: # refuse to accept something that doesn't parse as a pubkey return False, None unspent_list = self.getaddressunspent(address) utxos = { _utxo_name(utxo): utxo['value'] for utxo in unspent_list if utxo.get('height', -1) > 0 # all inputs must have at least 1 confirmation } for utxo in pk_inputs: val = utxos.get(utxo) if val is None: return False, None # utxo does not exist or was spent total += val answer = total >= amount if answer: return True, total return False, total except BaseException as e: #import traceback #traceback.print_exc() self.print_error("check_inputs_for_sufficient_funds: ", repr(e)) return None, None
def make_unsigned_transaction(self, amount, fee, all_inputs, outputs, changes): ''' make unsigned transaction ''' dust = self.dust_threshold( ) # always 546 for now, but this call is here in case something more sophisticated happens in the future coins = {} tx_inputs = [] amounts = {} try: for player in all_inputs: inputs_coins = self.get_coins(all_inputs[player]) # if there are no coins on input it terminates the process if inputs_coins: coins[player] = inputs_coins else: return None except BaseException as e: self.print_error('make_unsigned_transaction:', repr(e)) return None for player, pubkey_utxos in coins.items(): amounts[player] = 0 for pubkey, utxos in pubkey_utxos.items(): for utxo in utxos: utxo['type'] = 'p2pkh' utxo['address'] = Address.from_pubkey(pubkey) utxo['pubkeys'] = [pubkey] utxo['x_pubkeys'] = [pubkey] utxo['prevout_hash'] = utxo['tx_hash'] utxo['prevout_n'] = utxo['tx_pos'] utxo['signatures'] = [None] utxo['num_sig'] = 1 tx_inputs.append(utxo) amounts[player] += utxo['value'] tx_inputs.sort(key=lambda x: x['prevout_hash'] + str(x["tx_pos"])) tx_outputs = [(TYPE_ADDRESS, Address.from_string(output), int(amount)) for output in outputs] transaction = Transaction.from_io(tx_inputs, tx_outputs, sign_schnorr=False) tx_changes = [ (TYPE_ADDRESS, Address.from_string(changes[player]), int(amounts[player] - amount - fee)) for player in sorted(changes) if Address.is_valid(changes[player]) and int(amounts[player] - amount - fee) >= dust ] transaction.add_outputs(tx_changes) return transaction
def get_coins(self, inputs): coins = {} for public_key, utxos in inputs.items(): address = Address.from_pubkey(public_key) coins[public_key] = [ ] # FIXME: Should probably use a defaultdict here but maybe we want calling code to fail on KeyError ? unspent_list = self.getaddressunspent(address) utxo_dicts = { "{}:{}".format(utxo["tx_hash"], utxo["tx_pos"]): utxo for utxo in unspent_list } for utxo in utxos: utxo_dict = utxo_dicts.get(utxo) if utxo_dict: coins[public_key].append(utxo_dict) else: # uh-oh.. not found. may have been double-spent in the meantimg or buggy Player peer. return None return coins
def __init__(self, window, wallet, network_settings, period = 10.0, logger = None, password=None, timeout=60.0, typ=Messages.DEFAULT # NB: Only DEFAULT is currently supported ): super().__init__() cls = type(self) self.daemon = True self.timeout = timeout self.version = cls.PROTOCOL_VERSION + (1 if networks.net.TESTNET else 0) self.type = typ assert self.type == Messages.DEFAULT, "BackgroundShufflingThread currently only supports DEFAULT shuffles" cls.latest_shuffle_settings = cls.ShuffleSettings(self.type, Messages.TYPE_NAME_DICT[self.type], self.version, 0, 0, self.FEE) # set UPPER_BOUND and LOWER_BOUND from config keys here. Note all instances will see these changes immediately. cls.update_lower_and_upper_bound_from_config() self.period = period self.logger = logger self.wallet = wallet self.window = window self.host = network_settings.get("host", None) self.info_port = network_settings.get("info", None) self.port = 1337 # default value -- will get set to real value from server's stat port in run() method self.poolSize = 3 # default value -- will get set to real value from server's stat port in run() method self.banScore = 0 # comes from stats port -- our own personal ban score self.banned = False # comes from stats port. True if our IP is banned (default ban duration: 30 mins) self.ssl = network_settings.get("ssl", None) self.lock = threading.RLock() self.password = password self.threads = {scale:None for scale in self.scales} self.shared_chan = Channel(switch_timeout=None) # threads write a 3-tuple here: (killme_flg, thr, msg) self.stop_flg = threading.Event() self.last_idle_check = 0.0 # timestamp in seconds unix time self.done_utxos = dict() self._paused = False self._coins_busy_shuffling = set() # 'prevout_hash:n' (name) set of all coins that are currently being shuffled by a ProtocolThread. Both wallet locks should be held to read/write this. self._last_server_check = 0.0 # timestamp in seconds unix time self._dummy_address = Address.from_pubkey(EC_KEY(number_to_string(1337, generator_secp256k1.order())).get_public_key()) # dummy address # below 4 vars are related to the "delayed unreserve address" mechanism as part of the bug #70 & #97 workaround and the complexity created by it.. self._delayed_unreserve_addresses = dict() # dict of Address -> time.time() timestamp when its shuffle ended self._last_delayed_unreserve_check = 0.0 # timestamp in seconds unix time self._delayed_unreserve_check_interval = 60.0 # check these addresses every 60 seconds. self._delayed_unreserve_timeout = 600.0 # how long before the delayed-unreserve addresses expire; 10 minutes
def check_input_electrumx(network, inpcomp): """ Check an InputComponent against electrumx service. This can be a bit slow since it gets all utxos on that address. Returns normally if the check passed. Raises ValidationError if the input is not consistent with blockchain (according to server), and raises other exceptions if the server times out or gives an unexpected kind of response. """ address = Address.from_pubkey(inpcomp.pubkey) prevhash = inpcomp.prev_txid[::-1].hex() prevn = inpcomp.prev_index sh = address.to_scripthash_hex() u = network.synchronous_get(('blockchain.scripthash.listunspent', [sh]), timeout=5) for item in u: if prevhash == item['tx_hash'] and prevn == item['tx_pos']: break else: raise ValidationError('missing or spent or scriptpubkey mismatch') check(item['height'] > 0, 'not confirmed') check(item['value'] == inpcomp.amount, 'amount mismatch')