Forums

Python 3.3 - Unicode-objects must be encoded before hashing

I am trying to run this code in the bash console however returns always this error :

$ python transactions-server.py
Traceback (most recent call last):
  File "transactions-server.py", line 37, in <module>
    blockchain.append(create_genesis_block())
  File "transactions-server.py", line 31, in create_genesis_block
    }, "0")
  File "transactions-server.py", line 17, in __init__
    self.hash = self.hash_block()
  File "transactions-server.py", line 21, in hash_block
    sha.update(str(self.index) + str(self.timestamp) + str(self.data) + str(self.previous_hash))
TypeError: Unicode-objects must be encoded before hashing

SOURCE CODE :

from flask import Flask
from flask import request
import json
import requests
import hashlib as hasher
import datetime as date
node = Flask(__name__)

class Block:
  def __init__(self, index, timestamp, data, previous_hash):
    self.index = index
    self.timestamp = timestamp
    self.data = data
    self.previous_hash = previous_hash
    self.hash = self.hash_block()

  def hash_block(self):
    sha = hasher.sha256()
    sha.update(str(self.index) + str(self.timestamp) + str(self.data) + str(self.previous_hash))
    return sha.hexdigest()

def create_genesis_block():
 return Block(0, date.datetime.now(), {
    "proof-of-work": 9,
    "transactions": None
  }, "0")

miner_address = "q3nf394hjg-random-miner-address-34nf3i4nflkn3oi"
blockchain = []
blockchain.append(create_genesis_block())

this_nodes_transactions = []

peer_nodes = []
mining = True

@node.route('/txion', methods=['POST'])
def transaction():

  new_txion = request.get_json()
  this_nodes_transactions.append(new_txion)

  print ("New transaction")
  print ("FROM: {}").format(new_txion['from'].encode('ascii','replace'))
  print ("TO: {}").format(new_txion['to'].encode('ascii','replace'))
  print ("AMOUNT: {}\n").format(new_txion['amount'])
    return "Transaction submission successful\n"

@node.route('/blocks', methods=['GET'])
def get_blocks():
  chain_to_send = blockchain

  for i in range(len(chain_to_send)):
    block = chain_to_send[i]
    block_index = str(block.index)
    block_timestamp = str(block.timestamp)
    block_data = str(block.data)
    block_hash = block.hash
    chain_to_send[i] = {
      "index": block_index,
      "timestamp": block_timestamp,
      "data": block_data,
      "hash": block_hash
    }
  chain_to_send = json.dumps(chain_to_send)
  return chain_to_send

def find_new_chains():

  other_chains = []
  for node_url in peer_nodes:
   block = requests.get(node_url + "/blocks").content
     block = json.loads(block)

    other_chains.append(block)
  return other_chains

def consensus():

  other_chains = find_new_chains()

  longest_chain = blockchain
  for chain in other_chains:
    if len(longest_chain) < len(chain):
      longest_chain = chain

  blockchain = longest_chain

def proof_of_work(last_proof):

  incrementor = last_proof + 1

  while not (incrementor % 9 == 0 and incrementor % last_proof == 0):
    incrementor += 1

  return incrementor

@node.route('/mine', methods = ['GET'])
def mine():

  last_block = blockchain[len(blockchain) - 1]
  last_proof = last_block.data['proof-of-work']


  proof = proof_of_work(last_proof)

  this_nodes_transactions.append(
    { "from": "network", "to": miner_address, "amount": 1 }
  )

  new_block_data = {
    "proof-of-work": proof,
    "transactions": list(this_nodes_transactions)
  }
  new_block_index = last_block.index + 1
  new_block_timestamp = this_timestamp = date.datetime.now()
  last_block_hash = last_block.hash

  this_nodes_transactions[:] = []

  mined_block = Block(
    new_block_index,
    new_block_timestamp,
    new_block_data,
    last_block_hash
  )
  blockchain.append(mined_block)

  return json.dumps({
      "index": new_block_index,
      "timestamp": str(new_block_timestamp),
      "data": new_block_data,
      "hash": last_block_hash
  }) + "\n"

node.run()

[edit by admin: formatting]

is your code supposed to run on python3 or python2?

Run on python3 enable via bash : alias python="python3"

What happens if you run your code explicitly using Python 3.6, for example by running this in a bash console:

python3.6 transactions-server.py

...?

I run this in bash console however keeps same error ! https://www.pythonanywhere.com/shared_console/9d65ce3b-c32c-4dbd-9134-e82a5dbf8b30

Thanks

Ah, sorry! I was misreading the error message. So you're getting this:

  File "transactions-server.py", line 21, in hash_block
    sha.update(str(self.index) + str(self.timestamp) + str(self.data) + str(self.previous_hash))
TypeError: Unicode-objects must be encoded before hashing

What that is saying is that you're passing a unicode string instead of a byte string into the sha.update function.

So the solution is to convert the string into a byte string:

sha.update((str(self.index) + str(self.timestamp) + str(self.data) + str(self.previous_hash)).encode("utf-8"))

Sorry but there are some problem yet please see the bash console :
https://www.pythonanywhere.com/user/djalmabright/consoles/7822929/

It's not useful to post console links to the forums since no one except PythonAnywhere staff can see them. Post the error and what you're trying to do so that others can benefit.

Sorry I thought would be more clear however dont worry this is the error displayed in the run script :

$ python transactions-server.py Traceback (most recent call last):
File "transactions-server.py", line 36, in <module> blockchain.append(create_genesis_block()) File "transactions-server.py", line 30, in create_genesis_block }, "0") File "transactions-server.py", line 16, in init self.hash = self.hash_block() File "transactions-server.py", line 20, in hash_block sha.update(str(self.index) + str(self.timestamp) + str(self.data) + str(self.previous_hash).encode("utf-8")) TypeError: must be str, not bytes

Thanks !

that sounds like a coding issue that you will have to solve yourself?