text: import imports
  MINING_SENDER = "THE BLOCKCHAIN"
  MINING_REWARD = 1
  MINING_DIFFICULTY = 2
  
  
  class Blockchain:
  
      def __init__(self):
  
          self.transactions = []
          self.chain = []
          self.nodes = set()
          #Generate random number to be used as node_id
          self.node_id = str(imports.uuid4()).replace('-', '')
          #Create genesis block
          self.create_block(0, '00')
  
  
      def register_node(self, node_url):
          """
          Add a new node to the list of nodes
          """
          #Checking node_url has valid format
          parsed_url = imports.urlparse(node_url)
          if parsed_url.netloc:
              self.nodes.add(parsed_url.netloc)
          elif parsed_url.path:
              # Accepts an URL without scheme like '192.168.0.5:5000'.
              self.nodes.add(parsed_url.path)
          else:
              raise ValueError('Invalid URL')
  
  
      def verify_transaction_signature(self, sender_address, signature, transaction):
          """
          Check that the provided signature corresponds to transaction
          signed by the public key (sender_address)
          """
          try:
                  public_key = imports.RSA.importKey(imports.binascii.unhexlify(sender_address))
                  verifier = imports.PKCS1_v1_5.new(public_key)
                  h = imports.SHA.new(str(transaction).encode('utf8'))
                  return verifier.verify(h, imports.binascii.unhexlify(signature))
          except Exception as e:
                 return False
  
      def submit_transaction(self, sender_address, recipient_address, value, signature, MReward):
          """
          Add a transaction to transactions array if the signature verified
          """
          try:
              float(MReward)
              float(value)
          except Exception as e:
              return False
          transaction = imports.OrderedDict({'sender_address': sender_address,
                                      'recipient_address': recipient_address,
                                       'value': value,
                                       'miningReward': MReward})
  
          #Reward for mining a block
          if sender_address == MINING_SENDER:
              self.transactions.append(transaction)
              return len(self.chain) + 1
          #Manages transactions from wallet to another wallet
          else:
              transaction_verification = self.verify_transaction_signature(sender_address, signature, transaction)
              if transaction_verification:
                  self.transactions.append(transaction)
                  return len(self.chain) + 1
              else:
                  return False
  
  
      def create_block(self, nonce, previous_hash):
          """
          Add a block of transactions to the blockchain
          """
          block = {'block_number': len(self.chain) + 1,
                  'timestamp': imports.time(),
                  'transactions': self.transactions,
                  'nonce': nonce,
                  'previous_hash': previous_hash}
  
          # Reset the current list of transactions
          self.transactions = []
  
          self.chain.append(block)
          return block
      def mining_diffcultly(self):
          mreward = 0
          global MINING_DIFFICULTY
          for trans in self.transactions:
                mreward = mreward + float(trans['miningReward'])
          MINING_DIFFICULTY = MINING_DIFFICULTY + int(mreward//2)
  
      def hash(self, block):
          """
          Create a SHA-256 hash of a block
          """
          # We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes
          block_string = imports.json.dumps(block, sort_keys=True).encode()
  
          return imports.hashlib.sha256(block_string).hexdigest()
  
  
      def proof_of_work(self):
          """
          Proof of work algorithm
          """
          self.mining_diffcultly()
          last_block = self.chain[-1]
          last_hash = self.hash(last_block)
  
          nonce = 0
          while self.valid_proof(self.transactions, last_hash, nonce) is False:
              nonce += 1
  
          return nonce
  
  
      def valid_proof(self, transactions, last_hash, nonce, difficulty=MINING_DIFFICULTY):
          """
          Check if a hash value satisfies the mining conditions. This function is used within the proof_of_work function.
          """
          guess = (str(transactions)+str(last_hash)+str(nonce)).encode()
          guess_hash = imports.hashlib.sha256(guess).hexdigest()
          return guess_hash[:difficulty] == '0'*difficulty
  
  
      def valid_chain(self, chain):
          """
          check if a bockchain is valid
          """
          last_block = chain[0]
          current_index = 1
  
          while current_index < len(chain):
              block = chain[current_index]
              #print(last_block)
              #print(block)
              #print("\n-----------\n")
              # Check that the hash of the block is correct
              if block['previous_hash'] != self.hash(last_block):
                  return False
  
              # Check that the Proof of Work is correct
              #Delete the reward transaction
              transactions = block['transactions'][:-1]
              # Need to make sure that the dictionary is ordered. Otherwise we'll get a different hash
              transaction_elements = ['sender_address', 'recipient_address', 'value']
              transactions = [OrderedDict((k, transaction[k]) for k in transaction_elements) for transaction in transactions]
  
              if not self.valid_proof(transactions, block['previous_hash'], block['nonce'], MINING_DIFFICULTY):
                  return False
  
              last_block = block
              current_index += 1
  
          return True
  
      def resolve_conflicts(self):
          """
          Resolve conflicts between blockchain's nodes
          by replacing our chain with the longest one in the network.
          """
          neighbours = self.nodes
          new_chain = None
  
          # We're only looking for chains longer than ours
          max_length = len(self.chain)
  
          # Grab and verify the chains from all the nodes in our network
          for node in neighbours:
              print('http://' + node + '/chain')
              response = requests.get('http://' + node + '/chain')
  
              if response.status_code == 200:
                  length = response.json()['length']
                  chain = response.json()['chain']
  
                  # Check if the length is longer and the chain is valid
                  if length > max_length and self.valid_chain(chain):
                      max_length = length
                      new_chain = chain
  
          # Replace our chain if we discovered a new, valid chain longer than ours
          if new_chain:
              self.chain = new_chain
              return True
  
          return False
  