def get_addresses(self, testnet=False): """ Returns all addresses found in this script For output scripts, P2PKH scripts will return a single address the funds are being sent to. P2SH scripts will return a single address of the script the funds are being sent to. For input scripts, only standard signature and multi-signature scripts will return results: the address(es) used to sign. For standard signature scripts, a single address is returned while for multi-sig scripts, all n addresses in the redeem script are returned. Args: testnet (bool): True if the addresses are being used on testnet, False if used on mainnet. Returns: list: A list of Base58Check encoded bitcoin addresses. """ rv = [] # Determine script type if self.is_p2pkh(): version = self.P2PKH_TESTNET_VERSION if testnet else self.P2PKH_MAINNET_VERSION rv.append(key_hash_to_address(self.get_hash160(), version)) elif self.is_p2sh(): version = self.P2SH_TESTNET_VERSION if testnet else self.P2SH_MAINNET_VERSION rv.append(key_hash_to_address(self.get_hash160(), version)) elif self.is_multisig_sig(): # Extract out the info version = self.P2PKH_TESTNET_VERSION if testnet else self.P2PKH_MAINNET_VERSION sig_info = self.extract_multisig_sig_info() redeem_info = sig_info['redeem_script'].extract_multisig_redeem_info() for p in redeem_info['public_keys']: rv.append(key_hash_to_address(hash160(p), version)) # Also include the address of the redeem script itself. redeem_version = self.P2SH_TESTNET_VERSION if testnet else self.P2SH_MAINNET_VERSION rv.append(key_hash_to_address(sig_info['redeem_script'].hash160(), redeem_version)) elif self.is_p2pkh_sig(): version = self.P2PKH_TESTNET_VERSION if testnet else self.P2PKH_MAINNET_VERSION # Normal signature script... sig_info = self.extract_sig_info() rv.append(key_hash_to_address(hash160(sig_info['public_key']), version)) return rv
def get_addresses(self, testnet=False): """ Returns all addresses found in this script For output scripts, P2PKH scripts will return a single address the funds are being sent to. P2SH scripts will return a single address of the script the funds are being sent to. For input scripts, only standard signature and multi-signature scripts will return results: the address(es) used to sign. For standard signature scripts, a single address is returned while for multi-sig scripts, all n addresses in the redeem script are returned. Args: testnet (bool): True if the addresses are being used on testnet, False if used on mainnet. Returns: list: A list of Base58Check encoded bitcoin addresses. """ rv = [] # Determine script type if self.is_p2pkh(): version = self.P2PKH_TESTNET_VERSION if testnet else self.P2PKH_MAINNET_VERSION rv.append(key_hash_to_address(self.get_hash160(), version)) elif self.is_p2sh(): version = self.P2SH_TESTNET_VERSION if testnet else self.P2SH_MAINNET_VERSION rv.append(key_hash_to_address(self.get_hash160(), version)) elif self.is_multisig_sig(): # Extract out the info version = self.P2PKH_TESTNET_VERSION if testnet else self.P2PKH_MAINNET_VERSION sig_info = self.extract_multisig_sig_info() redeem_info = sig_info[ 'redeem_script'].extract_multisig_redeem_info() for p in redeem_info['public_keys']: rv.append(key_hash_to_address(hash160(p), version)) # Also include the address of the redeem script itself. redeem_version = self.P2SH_TESTNET_VERSION if testnet else self.P2SH_MAINNET_VERSION rv.append( key_hash_to_address(sig_info['redeem_script'].hash160(), redeem_version)) elif self.is_p2pkh_sig(): version = self.P2PKH_TESTNET_VERSION if testnet else self.P2PKH_MAINNET_VERSION # Normal signature script... sig_info = self.extract_sig_info() rv.append( key_hash_to_address(hash160(sig_info['public_key']), version)) elif Script.validate_template(self, [bytes, 'OP_CHECKSIG']): version = self.P2PKH_TESTNET_VERSION if testnet else self.P2PKH_MAINNET_VERSION rv.append(key_hash_to_address(hash160(self[0]), version)) return rv
def insert_txn(self, wallet_txn, mark_provisional=False): """ Inserts a transaction into the cache and updates the relevant dicts based on the addresses found in the transaction. Args: wallet_txn (WalletTransaction): A wallet transaction object. mark_provisional (bool): Marks the transaction as provisional (i.e. the transaction may have been built but not yet broadcast to the blockchain). Transactions marked as provisional are automatically pruned if they are not also seen by normal transaction updates/insertions within a certain time period. """ txid = str(wallet_txn.hash) # Check if it's already in with no change in status if txid in self._txn_cache and \ wallet_txn._serialize() == self._txn_cache[txid]._serialize(): return if mark_provisional and not wallet_txn.provisional: wallet_txn.provisional = int(time.time()) if not mark_provisional and wallet_txn.provisional: wallet_txn.provisional = False self._txn_cache[txid] = wallet_txn conf = wallet_txn.confirmations > 0 status = self.SPENT out_status = self.UNSPENT if not conf: status |= self.UNCONFIRMED out_status |= self.UNCONFIRMED if mark_provisional: status |= self.PROVISIONAL out_status |= self.PROVISIONAL # Get all the addresses for the transaction addrs = wallet_txn.get_addresses(self.testnet) if txid not in self._inputs_cache: self._inputs_cache[txid] = dict() if txid not in self._outputs_cache: self._outputs_cache[txid] = dict() for i, inp in enumerate(wallet_txn.inputs): self._inputs_cache[txid][i] = inp if inp.script.is_multisig_sig(): # Only keep the P2SH address sig_info = inp.script.extract_multisig_sig_info() redeem_version = Script.P2SH_TESTNET_VERSION if self.testnet \ else Script.P2SH_MAINNET_VERSION a = key_hash_to_address( sig_info['redeem_script'].hash160(), redeem_version) addrs['inputs'][i] = [a] # Update the status of any outputs out_txid = str(inp.outpoint) if out_txid not in self._outputs_cache: self._outputs_cache[out_txid] = {} if inp.outpoint_index not in self._outputs_cache[out_txid]: d = dict(output=None, status=status, spend_txid=txid, spend_index=i) self._outputs_cache[out_txid][inp.outpoint_index] = d else: x = self._outputs_cache[out_txid][inp.outpoint_index] x['status'] = status x['spend_txid'] = txid x['spend_index'] = i for i, out in enumerate(wallet_txn.outputs): if i in self._outputs_cache[txid]: o = self._outputs_cache[txid][i] o['output'] = out # Only update the status if it is unconfirmed unspent going # to confirmed unspent as it is possible that an input has # already marked it as spent. if not (o['status'] & self.SPENT): o['status'] = out_status else: self._outputs_cache[txid][i] = dict(output=out, status=out_status, spend_txid=None, spend_index=None) self._insert_txid(txid, addrs['inputs'], 'input') self._insert_txid(txid, addrs['outputs'], 'output') self._dirty = True
def insert_txn(self, wallet_txn, mark_provisional=False, expiration=0): """ Inserts a transaction into the cache and updates the relevant dicts based on the addresses found in the transaction. Args: wallet_txn (WalletTransaction): A wallet transaction object. mark_provisional (bool): Marks the transaction as provisional (i.e. the transaction may have been built but not yet broadcast to the blockchain). Transactions marked as provisional are automatically pruned if they are not also seen by normal transaction updates/insertions within a certain time period. expiration (int): Time, in seconds from epoch, when a provisional transaction should be automatically pruned. This is invalid unless mark_provisional=True. If expiration == 0, it is set to time.time() + PROVISIONAL_MAX_DURATION. This cannot be greater than PROVISIONAL_MAX_DURATION seconds in the future. """ txid = str(wallet_txn.hash) # Check if it's already in with no change in status if txid in self._txn_cache and \ wallet_txn._serialize() == self._txn_cache[txid]._serialize(): return if mark_provisional and not wallet_txn.provisional: now = time.time() if expiration < 0: raise ValueError("expiration cannot be negative.") if expiration == 0 or \ expiration > now + self.PROVISIONAL_MAX_DURATION: expiration = now + self.PROVISIONAL_MAX_DURATION wallet_txn.provisional = expiration if not mark_provisional and wallet_txn.provisional: wallet_txn.provisional = False self._txn_cache[txid] = wallet_txn conf = wallet_txn.confirmations > 0 status = self.SPENT out_status = self.UNSPENT if not conf: status |= self.UNCONFIRMED out_status |= self.UNCONFIRMED if mark_provisional: status |= self.PROVISIONAL out_status |= self.PROVISIONAL # Get all the addresses for the transaction addrs = wallet_txn.get_addresses(self.testnet) if txid not in self._inputs_cache: self._inputs_cache[txid] = dict() if txid not in self._outputs_cache: self._outputs_cache[txid] = dict() for i, inp in enumerate(wallet_txn.inputs): self._inputs_cache[txid][i] = inp if inp.script.is_multisig_sig(): # Only keep the P2SH address sig_info = inp.script.extract_multisig_sig_info() redeem_version = Script.P2SH_TESTNET_VERSION if self.testnet \ else Script.P2SH_MAINNET_VERSION a = key_hash_to_address(sig_info['redeem_script'].hash160(), redeem_version) addrs['inputs'][i] = [a] # Update the status of any outputs out_txid = str(inp.outpoint) if out_txid not in self._outputs_cache: self._outputs_cache[out_txid] = {} if inp.outpoint_index not in self._outputs_cache[out_txid]: d = dict(output=None, status=status, spend_txid=txid, spend_index=i) self._outputs_cache[out_txid][inp.outpoint_index] = d else: x = self._outputs_cache[out_txid][inp.outpoint_index] x['status'] = status x['spend_txid'] = txid x['spend_index'] = i for i, out in enumerate(wallet_txn.outputs): if i in self._outputs_cache[txid]: o = self._outputs_cache[txid][i] o['output'] = out # Only update the status if it is unconfirmed unspent going # to confirmed unspent as it is possible that an input has # already marked it as spent. if not (o['status'] & self.SPENT): o['status'] = out_status else: self._outputs_cache[txid][i] = dict(output=out, status=out_status, spend_txid=None, spend_index=None) self._insert_txid(txid, addrs['inputs'], 'input') self._insert_txid(txid, addrs['outputs'], 'output') self._dirty = True