text: import os
  import re
  from urllib import parse
  
  from eth_utils import (
      add_0x_prefix,
      remove_0x_prefix,
      is_integer,
  )
  
  from populus.utils.hexadecimal import hexbytes_to_hexstr
  from .filesystem import (
      normpath,
  )
  
  
  BASE_BLOCKCHAIN_STORAGE_DIR = "./chains"
  
  
  @normpath
  def get_base_blockchain_storage_dir(project_dir):
      base_blochcain_storage_dir = os.path.join(project_dir, BASE_BLOCKCHAIN_STORAGE_DIR)
      return base_blochcain_storage_dir
  
  
  BLOCK_OR_TRANSACTION_HASH_REGEX = "^(?:0x)?[a-zA-Z0-9]{64}$"
  
  
  def is_block_or_transaction_hash(value):
      return bool(re.match(BLOCK_OR_TRANSACTION_HASH_REGEX, value))
  
  
  BLOCK = 'block'
  TRANSACTION = 'transaction'
  
  
  def create_BIP122_uri(chain_id, resource_type, resource_identifier):
      """
      See: https://github.com/bitcoin/bips/blob/master/bip-0122.mediawiki
      """
      if resource_type not in {BLOCK, TRANSACTION}:
          raise ValueError("Invalid resource_type.  Must be one of 'block' or 'transaction'")
      elif not is_block_or_transaction_hash(resource_identifier):
          raise ValueError("Invalid resource_identifier.  Must be a hex encoded 32 byte value")
      elif not is_block_or_transaction_hash(chain_id):
          raise ValueError("Invalid chain_id.  Must be a hex encoded 32 byte value")
  
      return parse.urlunsplit([
          'blockchain',
          remove_0x_prefix(chain_id),
          "{0}/{1}".format(resource_type, remove_0x_prefix(resource_identifier)),
          '',
          '',
      ])
  
  
  def create_block_uri(chain_id, block_identifier):
      if is_integer(block_identifier):
          return create_BIP122_uri(chain_id, 'block', str(block_identifier))
      else:
          return create_BIP122_uri(chain_id, 'block', remove_0x_prefix(block_identifier))
  
  
  def create_transaction_uri(chain_id, transaction_hash):
      return create_BIP122_uri(chain_id, 'transaction', transaction_hash)
  
  
  def get_chain_id(web3):
      return hexbytes_to_hexstr(web3.eth.getBlock(0)['hash'])
  
  
  def get_chain_definition(web3, min_block_number=0, num_confirmations=0):
      """
      Return the blockchain URI that
      """
      chain_id = get_chain_id(web3)
      latest_block = web3.eth.getBlock('latest')
      latest_block_number = latest_block['number']
  
      target_block_number = latest_block_number - num_confirmations
      if target_block_number < min_block_number:
          raise ValueError("Cannot generate chain definition matching given constraints")
  
      block_for_definition = web3.eth.getBlock(target_block_number)
      block_hash_for_definition = hexbytes_to_hexstr(block_for_definition['hash'])
  
      return create_block_uri(chain_id, block_hash_for_definition)
  
  
  BIP122_URL_REGEX = (
      "^"
      "blockchain://"
      "(?P<chain_id>[a-zA-Z0-9]{64})"
      "/"
      "(?P<resource_type>block|transaction)"
      "/"
      "(?P<resource_hash>[a-zA-Z0-9]{64})"
      "$"
  )
  
  
  def is_BIP122_uri(value):
      return bool(re.match(BIP122_URL_REGEX, value))
  
  
  def parse_BIP122_uri(blockchain_uri):
      match = re.match(BIP122_URL_REGEX, blockchain_uri)
      if match is None:
          raise ValueError("Invalid URI format: '{0}'".format(blockchain_uri))
      chain_id, resource_type, resource_hash = match.groups()
      return (
          add_0x_prefix(chain_id),
          resource_type,
          add_0x_prefix(resource_hash),
      )
  
  
  def is_BIP122_block_uri(value):
      if not is_BIP122_uri(value):
          return False
      _, resource_type, _ = parse_BIP122_uri(value)
      return resource_type == BLOCK
  
  
  def is_BIP122_transaction_uri(value):
      if not is_BIP122_uri(value):
          return False
      _, resource_type, _ = parse_BIP122_uri(value)
      return resource_type == TRANSACTION
  
  
  def check_if_chain_matches_chain_uri(web3, blockchain_uri):
      chain_id, resource_type, resource_hash = parse_BIP122_uri(blockchain_uri)
      genesis_block = web3.eth.getBlock('earliest')
      if hexbytes_to_hexstr(genesis_block['hash']) != chain_id:
          return False
  
      if resource_type == BLOCK:
          resource = web3.eth.getBlock(resource_hash)
      elif resource_type == TRANSACTION:
          resource = web3.eth.getTransaction(resource_hash)
      else:
          raise ValueError("Unsupported resource type: {0}".format(resource_type))
  
      if hexbytes_to_hexstr(resource['hash']) == resource_hash:
          return True
      else:
          return False
  
  
  def is_synced(web3, allowed_block_delta=3):
      sync_info = web3.eth.syncing
      if not sync_info:
          return False
      blocks_to_be_synced = sync_info['highestBlock'] - sync_info['currentBlock']
      return blocks_to_be_synced <= allowed_block_delta
  