def add_metadata_to_tx(request, coin_symbol, tx_hash): if not is_valid_hash(tx_hash): return Http404 initial = {} form = BaseMetadataForm(initial=initial) if request.method == 'POST': form = BaseMetadataForm(data=request.POST) if form.is_valid(): metadata_key = form.cleaned_data.get('metadata_key') metadata_value = form.cleaned_data.get('metadata_value') results = put_metadata( metadata_dict={metadata_key: metadata_value}, tx_hash=tx_hash, coin_symbol=coin_symbol, api_key=BLOCKCYPHER_API_KEY, private=False, ) # import pprint; pprint.pprint(results, width=1) if results is True: msg = _('<pre>%(key)s</pre>-><pre>%(value)s</pre> succesfully uploaded to %(upload_string)s (<a href="#metadata">scroll down</a>)' % { 'key': metadata_key, 'value': metadata_value, 'upload_string': tx_hash, }) messages.success(request, msg, extra_tags='safe') redir_url = reverse( 'transaction_overview', kwargs={ 'coin_symbol': coin_symbol, 'tx_hash': tx_hash, }, ) return HttpResponseRedirect(redir_url) elif 'error' in results: messages.warning(request, results.get('error')) elif 'errors' in results: for error in results.get('errors'): messages.warning(request, error) elif request.method == 'GET': # Preseed tx hex if passed through GET string key = request.GET.get('k') value = request.GET.get('v') if key: initial['metadata_key'] = key if value: initial['metadata_value'] = value if key or value: form = BaseMetadataForm(initial=initial) return { 'form': form, 'is_input_page': True, 'coin_symbol': coin_symbol, 'tx_hash': tx_hash, }
def clean_search_string(self): search_string = self.cleaned_data['search_string'].strip() # get rid of non alphanumerics search_string = re.sub(r'[^a-zA-Z0-9]+', '', search_string) if is_valid_hash(search_string) or is_valid_address(search_string) or is_valid_block_num(search_string): return search_string else: err_msg = _('Not a valid address, transaction hash, or block number') raise forms.ValidationError(err_msg)
def clean(self): where_to_upload = self.cleaned_data.get('where_to_upload') upload_string = self.cleaned_data.get('upload_string') coin_symbol = self.cleaned_data.get('coin_symbol') if not where_to_upload or not upload_string: return self.cleaned_data if where_to_upload == 'address': if not is_valid_address_for_coinsymbol(upload_string, coin_symbol): cs_display = COIN_SYMBOL_MAPPINGS[coin_symbol]['display_name'] msg = _("Sorry, that's not a valid address for %(coin_symbol)s" % {'coin_symbol': cs_display}) raise forms.ValidationError(msg) elif where_to_upload == 'transaction': if not is_valid_hash(upload_string): msg = _("Sorry, that's not a valid transaction hash") raise forms.ValidationError(msg) elif where_to_upload == 'block': if not is_valid_hash(upload_string): msg = _("Sorry, that's not a valid block hash") raise forms.ValidationError(msg) else: raise Exception('Logic Fail: Not Possible') return self.cleaned_data
def clean_search_string(self): search_string = self.cleaned_data['search_string'].strip() # process possible Wallet Names if is_valid_wallet_name(search_string): return search_string # get rid of non alphanumerics search_string = re.sub(r'[^a-zA-Z0-9]+', '', search_string) if is_valid_hash(search_string) or is_valid_address(search_string) or is_valid_block_num(search_string): return search_string else: err_msg = _('Not a valid address, transaction hash, or block number') raise forms.ValidationError(err_msg)
def clean_search_string(self): search_string = self.cleaned_data['search_string'].strip() # process possible Wallet Names if is_valid_wallet_name(search_string): return search_string # get rid of non alphanumerics if search_string.lower().startswith("0x"): search_string = search_string[2:] search_string = re.sub(r'[^a-zA-Z0-9]+', '', search_string) if is_valid_hash(search_string) or is_valid_address(search_string) or is_valid_eth_address(search_string) or is_valid_block_num(search_string): return search_string else: err_msg = _('Not a valid address, transaction hash, or block number') raise forms.ValidationError(err_msg)
def home(request): form = SearchForm( initial={ 'search_string': '16Fg2yjwrbtC6fZp61EV9mNVKmwCzGasw5', 'coin_symbol': 'btc', }) if request.method == 'POST': form = SearchForm(data=request.POST) if form.is_valid(): redirect_url = None search_string = form.cleaned_data['search_string'] coin_symbol = form.cleaned_data['coin_symbol'] kwargs = {'coin_symbol': coin_symbol} if is_valid_block_num(search_string): kwargs['block_representation'] = search_string redirect_url = reverse('block_overview', kwargs=kwargs) elif is_valid_hash(search_string): if coin_symbol in SHA_COINS: if is_valid_sha_block_hash(search_string): kwargs['block_representation'] = search_string redirect_url = reverse('block_overview', kwargs=kwargs) else: kwargs['tx_hash'] = search_string redirect_url = reverse('transaction_overview', kwargs=kwargs) elif coin_symbol in SCRYPT_COINS: # Try to see if it's a valid TX hash tx_details = get_transaction_details( tx_hash=search_string, coin_symbol=coin_symbol, limit=1, api_key=BLOCKCYPHER_API_KEY, ) if 'error' in tx_details: # Not a valid TX hash, see if it's a block hash by checking blockchain block_details = get_block_overview( block_representation=search_string, coin_symbol=coin_symbol, txn_limit=1, api_key=BLOCKCYPHER_API_KEY, ) if 'error' in block_details: msg = _( "Sorry, '%(search_string)s' is not a valid transaction or block hash for %(currency)s" % { 'currency': coin_symbol, 'search_string': search_string, }) messages.error(request, msg) else: kwargs['block_representation'] = search_string redirect_url = reverse('block_overview', kwargs=kwargs) else: kwargs['tx_hash'] = search_string redirect_url = reverse('transaction_overview', kwargs=kwargs) elif is_valid_address(search_string): # It's an address kwargs['address'] = search_string first_char = search_string[0] # Override coin_symbol if we can infer it from the blockchain # There is now generic constants in the python library (constants.py) # Not migrating because this is custom (those constants have overlap/ambiguity) if first_char in ('1', ): # Do not force addresses starting with 3 to be BTC because that's also used by LTC kwargs['coin_symbol'] = 'btc' elif first_char in ('m', 'n', '2'): # Note that addresses starting in 2 can be LTC testnet, but since we don't support that it's okay to include kwargs['coin_symbol'] = 'btc-testnet' elif first_char in ('9', 'A'): kwargs['coin_symbol'] = 'doge' elif first_char in ('L', ): # Do not force addresses starting with 3 to be LTC because that's also used by BTC kwargs['coin_symbol'] = 'ltc' elif first_char in ('B', 'C'): kwargs['coin_symbol'] = 'bcy' redirect_url = reverse('address_overview', kwargs=kwargs) elif is_valid_wallet_name(search_string): addr = lookup_wallet_name(search_string, kwargs['coin_symbol']) if addr: kwargs['address'] = addr kwargs['wallet_name'] = search_string redirect_url = reverse('address_overview', kwargs=kwargs) else: msg = _("Sorry, that's not a valid wallet name") messages.error(request, msg) if redirect_url: return HttpResponseRedirect(redirect_url) else: currency = COIN_SYMBOL_MAPPINGS[ request.POST['coin_symbol']]['display_shortname'] msg = _( "Sorry, '%(search_string)s' is not a valid %(currency)s address, wallet name, transaction or block" % { 'currency': currency, 'search_string': request.POST['search_string'], }) messages.error(request, msg) return {'is_home': True, 'form': form}
def test_invalid_hash(self): assert not is_valid_hash(self.invalid_hash), self.invalid_hash
def test_valid_hash(self): assert is_valid_hash(self.valid_hash), self.valid_hash
def block_overview(request, coin_symbol, block_representation): TXNS_PER_PAGE = 20 # 1 indexed page current_page = request.GET.get('page') if current_page: current_page = int(current_page) else: current_page = 1 # TODO: fail gracefully if the user picks a number of pages that is too large # Waiting on @matthieu's change to API first (currently throws 502) try: if not is_valid_hash(block_representation): # it's a block num, we want this as a hash block_hash = get_block_hash( block_height=block_representation, coin_symbol=coin_symbol, api_key=BLOCKCYPHER_API_KEY, ) kwargs = { 'coin_symbol': coin_symbol, 'block_representation': block_hash, } redir_url = reverse('block_overview', kwargs=kwargs) return HttpResponseRedirect(redir_url) block_details = get_block_details( block_representation=block_representation, coin_symbol=coin_symbol, txn_limit=TXNS_PER_PAGE, in_out_limit=25, txn_offset=(current_page - 1) * TXNS_PER_PAGE, api_key=BLOCKCYPHER_API_KEY, ) except AssertionError: msg = _('Invalid Block Representation') messages.warning(request, msg) redir_url = reverse('coin_overview', kwargs={'coin_symbol': coin_symbol}) return HttpResponseRedirect(redir_url) # import pprint; pprint.pprint(block_details, width=1) if 'error' in block_details: msg = _('Sorry, that block was not found') messages.warning(request, msg) messages.warning(request, block_details['error']) return HttpResponseRedirect(reverse('home')) # Technically this is not the only API call used on this page api_url = 'https://api.blockcypher.com/v1/%s/%s/blocks/%s' % ( COIN_SYMBOL_MAPPINGS[coin_symbol]['blockcypher_code'], COIN_SYMBOL_MAPPINGS[coin_symbol]['blockcypher_network'], block_representation, ) return { 'coin_symbol': coin_symbol, 'api_url': api_url, 'block_details': block_details, 'current_page': current_page, 'max_pages': get_max_pages(num_items=block_details['n_tx'], items_per_page=TXNS_PER_PAGE), }
def home(request): form = SearchForm(initial={ 'search_string': '16Fg2yjwrbtC6fZp61EV9mNVKmwCzGasw5', 'coin_symbol': 'btc', }) if request.method == 'POST': form = SearchForm(data=request.POST) if form.is_valid(): redirect_url = None search_string = form.cleaned_data['search_string'] coin_symbol = form.cleaned_data['coin_symbol'] kwargs = {'coin_symbol': coin_symbol} if is_valid_block_num(search_string): kwargs['block_representation'] = search_string redirect_url = reverse('block_overview', kwargs=kwargs) elif is_valid_hash(search_string): if coin_symbol in SHA_COINS: if is_valid_sha_block_hash(search_string): kwargs['block_representation'] = search_string redirect_url = reverse('block_overview', kwargs=kwargs) else: kwargs['tx_hash'] = search_string redirect_url = reverse('transaction_overview', kwargs=kwargs) elif coin_symbol in SCRYPT_COINS: # Try to see if it's a valid TX hash tx_details = get_transaction_details( tx_hash=search_string, coin_symbol=coin_symbol, limit=1, api_key=BLOCKCYPHER_API_KEY, ) if 'error' in tx_details: # Not a valid TX hash, see if it's a block hash by checking blockchain block_details = get_block_overview( block_representation=search_string, coin_symbol=coin_symbol, txn_limit=1, api_key=BLOCKCYPHER_API_KEY, ) if 'error' in block_details: msg = _("Sorry, that's not a valid transaction or block hash for %(currency)s" % {'currency': coin_symbol}) messages.error(request, msg) else: kwargs['block_representation'] = search_string redirect_url = reverse('block_overview', kwargs=kwargs) else: kwargs['tx_hash'] = search_string redirect_url = reverse('transaction_overview', kwargs=kwargs) elif is_valid_address(search_string): # It's an address kwargs['address'] = search_string first_char = search_string[0] # Override coin_symbol if we can infer it from the blockchain # There is now generic constants in the python library (constants.py) # Not migrating because this is custom (those constants have overlap/ambiguity) if first_char in ('1', ): # Do not force addresses starting with 3 to be BTC because that's also used by LTC kwargs['coin_symbol'] = 'btc' elif first_char in ('m', 'n', '2'): # Note that addresses starting in 2 can be LTC testnet, but since we don't support that it's okay to include kwargs['coin_symbol'] = 'btc-testnet' elif first_char in ('9', 'A'): kwargs['coin_symbol'] = 'doge' elif first_char in ('L', ): # Do not force addresses starting with 3 to be LTC because that's also used by BTC kwargs['coin_symbol'] = 'ltc' elif first_char in ('U', ): kwargs['coin_symbol'] = 'uro' elif first_char in ('B', 'C'): kwargs['coin_symbol'] = 'bcy' redirect_url = reverse('address_overview', kwargs=kwargs) elif is_valid_wallet_name(search_string): addr = lookup_wallet_name(search_string, kwargs['coin_symbol']) if addr: kwargs['address'] = addr kwargs['wallet_name'] = search_string redirect_url = reverse('address_overview', kwargs=kwargs) else: msg = _("Sorry, that's not a valid wallet name") messages.error(request, msg) if redirect_url: return HttpResponseRedirect(redirect_url) else: currency = COIN_SYMBOL_MAPPINGS[request.POST['coin_symbol']]['display_shortname'] msg = _("Sorry, that's not a valid %(currency)s address, wallet name, transaction or block" % { 'currency': currency}) messages.error(request, msg) return { 'is_home': True, 'form': form }
def get_tx_url(tx_hash, coin_symbol): assert is_valid_coin_symbol(coin_symbol), coin_symbol assert is_valid_hash(tx_hash), tx_hash return 'https://live.blockcypher.com/%s/tx/%s/' % (coin_symbol, tx_hash)
def block_overview(request, coin_symbol, block_representation): TXNS_PER_PAGE = 20 # 1 indexed page current_page = request.GET.get('page') if current_page: current_page = int(current_page) else: current_page = 1 # TODO: fail gracefully if the user picks a number of pages that is too large # Waiting on @matthieu's change to API first (currently throws 502) try: if not is_valid_hash(block_representation): # it's a block num, we want this as a hash block_hash = get_block_hash( block_height=block_representation, coin_symbol=coin_symbol, api_key=BLOCKCYPHER_API_KEY, ) kwargs = { 'coin_symbol': coin_symbol, 'block_representation': block_hash, } redir_url = reverse('block_overview', kwargs=kwargs) return HttpResponseRedirect(redir_url) block_details = get_block_details( block_representation=block_representation, coin_symbol=coin_symbol, txn_limit=TXNS_PER_PAGE, in_out_limit=25, txn_offset=(current_page-1)*TXNS_PER_PAGE, api_key=BLOCKCYPHER_API_KEY, ) except AssertionError: msg = _('Invalid Block Representation') messages.warning(request, msg) redir_url = reverse('coin_overview', kwargs={'coin_symbol': coin_symbol}) return HttpResponseRedirect(redir_url) # import pprint; pprint.pprint(block_details, width=1) if 'error' in block_details: msg = _('Sorry, that block was not found') messages.warning(request, msg) messages.warning(request, block_details['error']) return HttpResponseRedirect(reverse('home')) # Technically this is not the only API call used on this page api_url = 'https://api.blockcypher.com/v1/%s/%s/blocks/%s' % ( COIN_SYMBOL_MAPPINGS[coin_symbol]['blockcypher_code'], COIN_SYMBOL_MAPPINGS[coin_symbol]['blockcypher_network'], block_representation, ) return { 'coin_symbol': coin_symbol, 'api_url': api_url, 'block_details': block_details, 'current_page': current_page, 'max_pages': get_max_pages(num_items=block_details['n_tx'], items_per_page=TXNS_PER_PAGE), }