cool hit counter Creating a blockchain from scratch in Python_Intefrankly

Creating a blockchain from scratch in Python


The content of this article was primarily translated from Learn Blockchains by Building One.

The fastest way to learn the blockchain is to create one yourself.

We've all been surprised and amazed at the rise of Bitcoin and wondered how the technology behind it - the blockchain - was made possible.

But fully figuring out blockchain is not easy, at least for me. I like to learn by doing, and I get a stronger grasp on technology by writing code. Understanding of blockchain can be deepened by building a blockchain.

preliminary

We know that a blockchain is an immutable, ordered chain structure consisting of records of blocks, which can be transactions, files, or whatever data you want, and it is important that they are linked by hashes (hash).

Reading this article requires the reader to have a basic understanding of Python, be able to write basic Python code, and need to have a basic understanding of HTTP requests.

Environmental preparation

Make sure you have Python 3.6+, pip , Flask, requests installed.

Installation method.

pip install Flask==0.12.2 requests==2.18.4

An HTTP client such as Postman, cURL or other client is also required.

I. Start creating BlockChain

Open your favorite text editor or IDE, such as PyCharm, and create a new file, blockchain.py, in which all the code for this article is written.

BlockChain class

First create a Blockchain class with two lists created in the constructor, one for storing the blockchain and one for storing transactions.

The following is the framework for the BlockChain class.

classBlockchain(object):

def__init__(self):

self.chain = []

self.current_transactions = []

defnew_block(self):

# Creates a new Block and adds it to the chain

pass

defnew_transaction(self):

# Adds a new transaction to the list of transactions

pass

@staticmethod

defhash(block):

# Hashes a Block

pass

@property

deflast_block(self):

# Returns the last Block in the chain

pass

The Blockchain class is used to manage the chain, it can store transactions, add new blocks, etc. Let's refine these methods further below.

block structure

Each block contains attributes: an index, a Unix timestamp, a list of transactions, a proof of workload (explained later), and the Hash value of the previous block.

Here is a district block structure:

block = {

'index':1,

'timestamp':1506057125.900785,

'transactions': [

{

'sender':"8527147fe1f5426f9dd545de4b27ee00",

'recipient':"a77f5cdfa2934df3954a5c7c7da5df1f",

'amount':5,

}

],

'proof':324984774000,

'previous_hash':"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"

}

By this point, the concept of blockchain is clear; each new block contains the Hash of the previous block, which is the key point that guarantees blockchain immutability. If an attacker corrupts one of the previous blocks, then the Hash of all subsequent blocks becomes incorrect.

Join the deal

Next we need to add a transaction to refine the new_transaction method

classBlockchain(object):

...

defnew_transaction(self, sender, recipient, amount):

"""

Generate new transaction information and the information will be added to the next block to be mined

:param sender: Address of the Sender

:param recipient: Address of the Recipient

:param amount: Amount

:return: The index of the Block that will hold this transaction

"""

self.current_transactions.append({

'sender': sender,

'recipient': recipient,

'amount': amount,

})

returnself.last_block['index'] +1

method adds a transaction record to the list and returns the index of the block to which the record will be added (the next block to be mined), which will be useful later when the user submits the transaction.

Creating Blocks

When Blockchain is instantiated, we need to construct a genesis block (the first block without a pre-block) and add a proof of workload to it.

Each block is subject to proof of workload, commonly known as mining, which will be continued later.

In order to construct the creation block, we also need to refine the new_block(), new_transaction() and hash() methods.

importhashlib

importjson

fromtimeimporttime

classBlockchain(object):

def__init__(self):

self.current_transactions = []

self.chain = []

# Create the genesis block

self.new_block(previous_hash=1, proof=100)

defnew_block(self, proof, previous_hash=None):

"""

Generate new blocks

:param proof: The proof given by the Proof of Work algorithm

:param previous_hash: (Optional) Hash of previous Block

:return: New Block

"""

block = {

'index': len(self.chain) +1,

'timestamp': time(),

'transactions': self.current_transactions,

'proof': proof,

'previous_hash': previous_hashorself.hash(self.chain[-1]),

}

# Reset the current list of transactions

self.current_transactions = []

self.chain.append(block)

returnblock

defnew_transaction(self, sender, recipient, amount):

"""

Generate new transaction information and the information will be added to the next block to be mined

:param sender: Address of the Sender

:param recipient: Address of the Recipient

:param amount: Amount

:return: The index of the Block that will hold this transaction

"""

self.current_transactions.append({

'sender': sender,

'recipient': recipient,

'amount': amount,

})

returnself.last_block['index'] +1

@property

deflast_block(self):

returnself.chain[-1]

@staticmethod

defhash(block):

"""

Generate the SHA-256 hash value of the block

:param block: Block

:return:

"""

# We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes

block_string = json.dumps(block, sort_keys=True).encode()

returnhashlib.sha256(block_string).hexdigest()

The code and comments above provide a visual understanding of the blockchain, next we look at how the blocks are mined.

Understanding workload proofs

The new block relies on a proof-of-work algorithm (PoW) to construct it. The goal of PoW is to find a number that meets a specific condition, a number that is hard to calculate but easy to verify. This is the core idea of workload proofs.

To make it easier to understand, let's take an example.

Suppose the Hash of the product of an integer x times another integer y must end in 0, i.e. hash(x * y) = ac23dc... 0. Given the variable x = 5, find the value of y?

Implemented in Python as follows.

fromhashlibimportsha256

x =5

y =# y unknown

whilesha256(f''.encode()).hexdigest()[-1] !="0":

y +=1

print(f'The solution is y = ')

The result y = 21, because.

hash(5 * 21) = 1253e9373e...5e3600155e860

In Bitcoin, the proof-of-work algorithm called Hashcash is used, which is very similar to the problem above. Miners compete to calculate the results for the right to create blocks. Usually, the difficulty of the computation is proportional to the number of specific characters that need to be satisfied by the target string, and miners are rewarded with bitcoins when they calculate the result.

Of course, it's very easy to verify this result on the web.

Achieving proof of workload

Let us implement a similar PoW algorithm with the following rules.

Find a number p such that the Hash value of the string it splices into with the proof of the previous block starts with 4 zeros.

importhashlib

importjson

fromtimeimporttime

fromuuidimportuuid4

classBlockchain(object):

...

defproof_of_work(self, last_proof):

"""

A simple proof of workload :

- Find a p' feasible hash(pp') with4 size0 beginning

- p It's a proof of the last block, p' It's current proof.

:param last_proof:

:return:

"""

proof =

whileself.valid_proof(last_proof, proof)isFalse:

proof +=1

returnproof

@staticmethod

defvalid_proof(last_proof, proof):

"""

Verify proof: Does hash(last_proof, proof) start with 4 zeros?

:param last_proof: Previous Proof

:param proof: Current Proof

:return: True if correct, False if not.

"""

guess =f''.encode()

guess_hash = hashlib.sha256(guess).hexdigest()

returnguess_hash[:4] =="0000"

A measure of the complexity of the algorithm is to modify the number of zeros that begin with. Use 4 for demonstration purposes, and you will find that any additional zeroes will significantly increase the time required to calculate the result.

Now that the Blockchain class is basically done, the next step is to use HTTP requests to interact with it.

II. BlockChain as an API interface

We will be using the Python Flask framework, a lightweight web application framework that facilitates mapping web requests to Python functions, now let's get Blockchain running on the Flask web based.

We will create three interfaces.

/transactions/new creates a transaction and adds it to the block

/mine tells the server to mine the new block

/chain returns the entire blockchain

Create Node

Our Flask server will play a node in the blockchain network. Let's start by adding some framework code.

importhashlib

importjson

fromtextwrapimportdedent

fromtimeimporttime

fromuuidimportuuid4

fromflaskimportFlask

classBlockchain(object):

...

# Instantiate our Node

app = Flask(__name__)

# Generate a globally unique address for this node

node_identifier = str(uuid4()).replace('-','')

# Instantiate the Blockchain

blockchain = Blockchain()

@app.route('/mine', methods=['GET'])

defmine():

return"We'll mine a new Block"

@app.route('/transactions/new', methods=['POST'])

defnew_transaction():

return"We'll add a new transaction"

@app.route('/chain', methods=['GET'])

deffull_chain():

response = {

'chain': blockchain.chain,

'length': len(blockchain.chain),

}

returnjsonify(response),200

if__name__ =='__main__':

app.run(host='127.0.0.1', port=5000)

To briefly illustrate the above code.

Line 15: Create a node.

Line 18: Create a random name for the node.

Line 21: Instance of Blockchain class.

Lines 24-26: create/mine GET interface.

Lines 28-30: Create /transactions/new POST interface, which sends transaction data to the interface.

Lines 32-38: Creates the /chain interface, returning the entire blockchain.

Lines 40-41: The service is running on port 5000.

Sending transactions

The structure of the transaction data sent to the node is as follows.

{

"sender":"my address",

"recipient":"someone else's address",

"amount":5

}

There is already a way to add transactions before, and it's easy to add transactions based on the interface

importhashlib

importjson

fromtextwrapimportdedent

fromtimeimporttime

fromuuidimportuuid4

fromflaskimportFlask, jsonify, request

...

@app.route('/transactions/new', methods=['POST'])

defnew_transaction():

values = request.get_json()

# Check that the required fields are in the POST'ed data

required = ['sender','recipient','amount']

ifnotall(kinvaluesforkinrequired):

return'Missing values',400

# Create a new Transaction

index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])

response = {'message':f'Transaction will be added to Block '}

returnjsonify(response),201

dig for coal or minerals

dig for coal or minerals That's where the magic is., It's simple., Did the following three things:

Calculating the Proof of Workload PoW

Grant a miner (yourself) a coin by adding a new transaction

Constructing a new block and adding it to the chain

importhashlib

importjson

fromtimeimporttime

fromuuidimportuuid4

fromflaskimportFlask, jsonify, request

...

@app.route('/mine', methods=['GET'])

defmine():

# We run the proof of work algorithm to get the next proof...

last_block = blockchain.last_block

last_proof = last_block['proof']

proof = blockchain.proof_of_work(last_proof)

# Reward nodes for proof of workload.

# The sender is "0" Indicates a newly mined coin

blockchain.new_transaction(

sender="0",

recipient=node_identifier,

amount=1,

)

# Forge the new Block by adding it to the chain

block = blockchain.new_block(proof)

response = {

'message':"New Block Forged",

'index': block['index'],

'transactions': block['transactions'],

'proof': block['proof'],

'previous_hash': block['previous_hash'],

}

returnjsonify(response),200

Note that the recipient of the transaction is our own server node, and most of the work we do is just interacting around the Blockchain class methods. At this point, our blockchain is complete, so let's run it for real

III. Running the blockchain

You can use cURL or Postman to interact with the API

Start server:

$python blockchain.py

* Runing on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Let's go through the request http://localhost:5000/mine come to dig for coal or minerals

Add a new transaction via post request

If you are not using Postman, the same cURL statement is used.

$ curl -X POST -H"Content-Type: application/json"-d'{

"sender": "d4ee26eee15148ee92c6cd394edd974e",

"recipient": "someone-other-address",

"amount": 5

}'"http://localhost:5000/transactions/new"

After mining twice, there are 3 blocks, and you can get all the block information by requesting http://localhost:5000/chain.

{

"chain": [

{

"index":1,

"previous_hash":1,

"proof":100,

"timestamp":1506280650.770839,

"transactions": []

},

{

"index":2,

"previous_hash":"c099bc...bfb7",

"proof":35293,

"timestamp":1506280664.717925,

"transactions": [

{

"amount":1,

"recipient":"8bbcb347e0634905b0cac7955bae152b",

"sender":"0"

}

]

},

{

"index":3,

"previous_hash":"eff91a...10f2",

"proof":35089,

"timestamp":1506280666.1086972,

"transactions": [

{

"amount":1,

"recipient":"8bbcb347e0634905b0cac7955bae152b",

"sender":"0"

}

]

}

],

"length":3

}

IV. Coherence (consensus)

terrific, We already have a basic blockchain that can accept transactions and dig for coal or minerals。 But blockchain systems are supposed to be distributed。 Since it's a distributed, So what exactly do we take to guarantee that all nodes have the same chain? It's all about consistency., We want to have multiple nodes on the network, It would be necessary to implement a consistent algorithm。

Register Node

Before implementing the consistency algorithm, we need to find a way to make a node aware of its neighboring nodes. Each node needs to keep a record containing the other nodes in the network. So let's add a couple of new interfaces.

/nodes/register Receive a list of new nodes in the form of URLs

/nodes/resolve performs a consistency algorithm that resolves any conflicts and ensures that the node has the correct chain

Let's modify it.Blockchain ofinit function and provide a Register Node approach:

We use set to store nodes, which is a simple way to avoid adding nodes repeatedly.

Implementing consensus algorithms

As mentioned earlier, conflict means that different nodes have different chains, and to solve this problem, it is stipulated that the longest, valid chain is the final chain; in other words, the valid longest chain in the network is the actual chain.

We use the following algorithm to reach consensus in the network

...

importrequests

classBlockchain(object)

...

defvalid_chain(self, chain):

"""

Determine if a given blockchain is valid

:param chain:

A blockchain

:return: True if valid, False if not

"""

last_block = chain[]

current_index =1

whilecurrent_index

block = chain[current_index]

print(f'')

print(f'')

print(" ----------- ")

# Check that the hash of the block is correct

ifblock['previous_hash'] != self.hash(last_block):

returnFalse

# Check that the Proof of Work is correct

ifnotself.valid_proof(last_block['proof'], block['proof']):

returnFalse

last_block = block

current_index +=1

returnTrue

defresolve_conflicts(self):

"""

Consensus algorithm for conflict resolution

Use the longest chain in the network .

:return: True if the chain is replaced, False otherwise

"""

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

fornodeinneighbours:

response = requests.get(f'http:///chain')

ifresponse.status_code ==200:

length = response.json()['length']

chain = response.json()['chain']

# Check if the length is longer and the chain is valid

iflength > max_lengthandself.valid_chain(chain):

max_length = length

new_chain = chain

# Replace our chain if we discovered a new, valid chain longer than ours

ifnew_chain:

self.chain = new_chain

returnTrue

returnFalse

The first method valid_chain() is used to check if it is a valid chain, iterating through each block to verify the hash and proof.

The 2nd method resolve_conflicts() is used to resolve conflicts, iterate through all neighboring nodes and check the validity of the chain with the previous method. Replace your own chain if you find a valid longer chain

Let's add two routes, One for Register Node, One for conflict resolution。

@app.route('/nodes/register', methods=['POST'])

defregister_nodes():

values = request.get_json()

nodes = values.get('nodes')

ifnodesisNone:

return"Error: Please supply a valid list of nodes",400

fornodeinnodes:

blockchain.register_node(node)

response = {

'message':'New nodes have been added',

'total_nodes': list(blockchain.nodes),

}

returnjsonify(response),201

@app.route('/nodes/resolve', methods=['GET'])

defconsensus():

replaced = blockchain.resolve_conflicts()

ifreplaced:

response = {

'message':'Our chain was replaced',

'new_chain': blockchain.chain

}

else:

response = {

'message':'Our chain is authoritative',

'chain': blockchain.chain

}

returnjsonify(response),200

You can run the nodes on different machines or open different network ports on one machine to simulate a multi-node network, as demonstrated here by opening different ports on the same machine and running a single command in a different terminal to start two nodes: http://localhost:5000 and http://localhost:5001

pipenv run python blockchain.py

pipenv run python blockchain.py -p 5001

Then two blocks are mined on node 2 to ensure it is a longer chain, and then the interface /nodes/resolve is accessed on node 1 ,at which point node 1's chain is replaced by node 2's chain via the consensus algorithm.

Well, you can invite your friends to come along and test your blockchain.


Recommended>>
1、linux下的别名设置
2、Blockchain development company talks about crossborder payments
3、Python Data Structures and Algorithms Notes 3
4、Use of the hdfsdfsls command
5、Game Theory Advancement for AntiSG Games and SJ Theorem

    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号