A Block Producer Smart Contract (BPSC) exists in a plain “smart blockchain” network. To make a successful transaction in the network, the sender must first transfer the cryptocurrency to the (BPSC) wallet, and immediately the (BPSC) will automatically send the same cryptocurrency to the final recipient’s wallet. This seemingly simple task saves accurate and impervious data from all successful transactions in the (BPSC) Smart Contract. The present article was written in April 2020 by Somayyeh Gholami and Mehran Kazeminia in two versions in English and Persian.
If you are unfamiliar with “smart blockchain” and have not read our previous articles on this technology, you can see the outline of the subject in the following article. However, the necessary explanations are given during the implementation
https://hackernoon.com/what-is-smart-blockchain-4b134275e90f
We assume that the reader is familiar with the Python language and comprehend blockchain concepts, hash, and so on. For this reason and to avoid getting the story long and tedious, we’re focusing on “smart blockchain” features. Of course, if you forgot some of the concepts of blockchain, you can refer to the following article. In this article, the author has fully implemented a blockchain with Python. We also use his ideas and codes to implement “smart blockchain” so that we can easily compare “smart blockchain” with blockchain.
https://hackernoon.com/learn-blockchains-by-building-one-117428612f46
Our Python version is Python 3.7 and we use Spyder-Anaconda3 as the IDE. You can use your favorite IDE like PyCharm and so on. Of course, for example, Jupyter Notebook is not suitable for this purpose. We will also use Postman, but you can also use cURL
Let’s get started and create a class called Smart_Blockchain. The constructor method of this class currently creates two empty lists, one for storing transactions and the other for storing chain of blocks. Meanwhile, in the constructor method of this class, we also write the code for creating the initial genesis block. Now we need to complete the following four methods:
new_block, new_transaction, last_block, hash
But there are no consensus issues in “Smart Blockchain” and neither the proof-of-work (POW) mechanism nor the proof-of-stock (POS) mechanism nor … needed. In other words, there is no need to produce a couple of alternatives of a single block like the Bitcoin and Ethereum network to let miners can choose a valid and correct block through spending billions of dollars and staggering power consumption. The following code is set exactly based on these features.
import hashlib
import json
from time import time
class Smart_Blockchain:
def __init__(self):
self.current_transactions = []
self.chain = []
# Create the genesis block
self.new_block(previous_hash='1')
def new_block(self, previous_hash):
"""
Create a new Block in the Smart Blockchain
:param previous_hash: Hash of previous Block
:return: New Block
"""
block = {
'index': len(self.chain) + 1,
'timestamp': time(),
'transactions': self.current_transactions,
'previous_hash': previous_hash or self.hash(self.chain[-1]),
}
# Reset the current list of transactions
self.current_transactions = []
self.chain.append(block)
return block
def new_transaction(self, sender, amount, recipient):
"""
Creates a new transaction to go into the next mined Block
:param sender: Address of the Sender
:param amount_send: The amount sent by the sender
:param bpsc: Address of the Smart contract (bpsc)
:param amount_bpsc: The amount received by bpsc (Transaction fees)
:param recipient: Address of the Recipient
:param amount_receive: The amount received by the recipient
:return: The index of the Block that will hold this transaction
"""
self.current_transactions.append({
'sender': sender,
'amount_send': amount,
'bpsc': 'bpsc_wallet_address', # Block Producer Smart Contract (bpsc)
'amount_bpsc': amount * 0.00005, # Transaction fees
'recipient': recipient,
'amount_receive': amount * 0.99995,
})
return self.last_block['index'] + 1
@property
def last_block(self):
return self.chain[-1]
@staticmethod
def hash(block):
"""
Creates a SHA-256 hash of a Block
:param block: Block
"""
# We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes
block_string = json.dumps(block, sort_keys=True).encode()
return hashlib.sha256(block_string).hexdigest()
In the above codes, there is no proof variable or nonce and there is no need to block reward. Because, as mentioned earlier, there is no consensus on “smart blockchain.” Even transaction fees could be zero. But to avoid spamming, for example, we consider 0.00005 of the total cost of each transaction. This small amount is stored in the (BPSC) smart contract, and the total cost of transactions can be used for development, etc., or even burned to create negative inflation. Negative inflation is one of the most effective ways to maintain and strengthen the value of money as well as for cryptocurrencies.
We are now using the Flask framework so that we can communicate with the “smart blockchain” through requests based on the HTTP protocol platform. To do this, we create the following three methods:
/transactions/new
Request to build a new transaction and add it to a block
/mine
Ask our server to mine a new block
/chain
Request to return a complete list of blocks
import hashlib
import json
from time import time
from urllib.parse import urlparse
from flask import Flask, jsonify, request
class Smart_Blockchain:
# .....
# .....
# .....
# Instantiate the Node
app = Flask(__name__)
# Instantiate the Smart_Blockchain
blockchain = Smart_Blockchain()
@app.route('/mine', methods=['GET'])
def mine():
last_block = blockchain.last_block
# Forge the new Block by adding it to the chain
previous_hash = blockchain.hash(last_block)
block = blockchain.new_block(previous_hash)
response = {
'message': "New Block Forged",
'index': block['index'],
'transactions': block['transactions'],
'previous_hash': block['previous_hash'],
}
return jsonify(response), 200
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
values = request.get_json()
# Check that the required fields are in the POST'ed data
required = ['sender', 'amount', 'recipient']
if not all(k in values for k in required):
return 'Missing values', 400
# Create a new Transaction
index = blockchain.new_transaction(values['sender'], values['amount'], values['recipient'])
response = {'message': f'Transaction will be added to Block {index}'}
return jsonify(response), 201
@app.route('/chain', methods=['GET'])
def full_chain():
response = {
'chain': blockchain.chain,
'length': len(blockchain.chain),
}
return jsonify(response), 200
if __name__ == '__main__':
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('-p', '--port', default=5000, type=int, help='port to listen on')
args = parser.parse_args()
port = args.port
app.run(host='0.0.0.0', port=port)
According to the above code, the server will be launched on port 5000. Of course, another way is to import sys and make the following changes to the end codes. With this, you can always specify your desired port when running.
import sys
.....
if __name__ == '__main__':
app.run(host='0.0.0.0', port=sys.argv[1])
Now we concatenate the two main parts of the code. Of course, you can download all the code for this article here, and at this point, our file name is bpsc101.py. In the next step, we will run the server. We also use Postman to connect to the new API on the network.
We will start communicating with the new API by sending a POST transaction request to our server address:
http://localhost:5000/transactions/new
The information for this transaction must be entered in the Body section, and obviously, we select raw and then JSON.
In the example above, we wanted to send one million tokens (for example, Aka tokens) from our wallet to another person (with a specific address). To do this, we sent the tokens and the request for this transaction to the server (which acts as a Block Producer Smart Contract). As you see, the server responded that this transaction would be added to the second block. Because the Genesis Block has already been defined in the code.
We will now send a GET mining request to our server address: http://localhost:5000/mine
This will create the next block. The content of each block includes all performed transactions and pending ones, for instance, here, the second block is created that contains only one transaction.
According to the image above, the amount of fifty tokens for the transaction fee remains in the (BPSC) wallet, and the rest of the amount is sent to the final recipient by (BPSC). Of course, as mentioned before, the transaction fees will either be burned or spent on network development and so on. However, the type of spending these amounts has not been modeled yet. Now, We know that there must be two blocks in our chain’s server. To see the current chain, we send a GET chain request to our server address:
http://localhost:5000/chain
For further testing, you can send new requests for the transaction, and each time after a few transaction requests, you can send a mining request. Even, without having to do a transaction, you can mine an empty block. Meanwhile, the chain request determines the status of the chain for you at any time.
The “smart blockchain” network like Blockchain is decentralized. That’s why we need to implement a way for each node to be informed of its neighbor nodes on the network and keep a list of them. To do this, we use the set() so, each node recorded only once. We also create the following method to accept a list of new nodes in the form of URLs:
/nodes/register
import hashlib
import json
from time import time
from urllib.parse import urlparse
from flask import Flask, jsonify, request
class Smart_Blockchain:
def __init__(self):
self.current_transactions = []
self.chain = []
self.nodes = set()
# Create the genesis block
self.new_block(previous_hash='1')
def register_node(self, address):
"""
Add a new node to the list of nodes
:param address: Address of node. Eg. 'http://192.168.0.5:5000'
"""
parsed_url = urlparse(address)
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')
.......
@app.route('/nodes/register', methods=['POST'])
def register_nodes():
values = request.get_json()
nodes = values.get('nodes')
if nodes is None:
return "Error: Please supply a valid list of nodes", 400
for node in nodes:
blockchain.register_node(node)
response = {
'message': 'New nodes have been added',
'total_nodes': list(blockchain.nodes),
}
return jsonify(response), 201
if __name__ == '__main__':
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('-p', '--port', default=5000, type=int, help='port to listen on')
args = parser.parse_args()
port = args.port
app.run(host='0.0.0.0', port=port)
Remember that in “smart blockchain” only the block producer smart contract of (BPSC) can create or mine the new block. But, the rest nodes of the network can only send a request to (BPSC) and through this receive the last chain. So we need to implement the nodes request from (BPSC) to return the last chain. To do this, we create the following method:
/smart/chain
import hashlib
import json
from time import time
from urllib.parse import urlparse
import requests
from flask import Flask, jsonify, request
class Smart_Blockchain:
def __init__(self):
self.current_transactions = []
self.chain = []
self.nodes = set()
# Create the genesis block
self.new_block(previous_hash='1')
def smart_chain(self):
"""
All nodes can receive the smart_chain
"""
schain = None
response = requests.get(f'http://127.0.0.1:5000/chain')
if response.status_code == 200:
chain = response.json()['chain']
schain = chain
# Replace our chain
if schain:
self.chain = schain
return True
return False
.......
@app.route('/smart/chain', methods=['GET'])
def smart_chain():
replaced = blockchain.smart_chain()
if replaced:
response = {
'message': 'Smart chain update by bpsc',
'smart chain': blockchain.chain,
'length': len(blockchain.chain)
}
else:
response = {
'message': 'Unsuccessful: Please try again',
'old chain': blockchain.chain,
'length': len(blockchain.chain)
}
return jsonify(response), 200
if __name__ == '__main__':
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('-p', '--port', default=5000, type=int, help='port to listen on')
args = parser.parse_args()
port = args.port
app.run(host='0.0.0.0', port=port)
As mentioned earlier, in this example, we want to launch our server (which acts as a block producer smart contract) on the port 5000. According to the above codes, any other nodes in the network can send a request to (BPSC) and receive the last chain. The command “requests” on line 26 do the same. In other words, with one request, the last chain created by (BPSC) will replace the node’s chain. To continue, we need to concatenate the pieces of code (up to this point). You can download all the code for this article here, and so far bpsc102.py is the name of our file.
But, if we want to test the /nodes/register method as well as the /smart/chain method, we need to have other nodes in the network. This means that we either have to use several machines or define different ports on the same machine. We do the second one and create the other two nodes with the port numbers 5001 and 5002 on the same machine. But, the main problem is that except the (BPSC) which is responsible for making the transaction, mining the block, and creating a chain for specific tokens (such as Aka tokens), the rest of the nodes shouldn’t be able to do such activities. Of course, it is simple and we just need to remove some of the code from other nodes such as the ability of mining and…
To do this, we created the files: nodes_v1_5001.py and the nodes_v1_5002.py. Please see the details of these two files by clicking on them, as these modifications are not included in the article for avoiding context lengthier. In these files, compared to the original bpsc102.py, only some of the code has been deleted and nothing has been added yet. Of course, the port number has also changed.
If you are using a VPN for any reason, you must turn it off during subsequent tests. Anyway, run all three files on separate consoles and create three nodes. This means that we will have the following nodes at the same time:
http://localhost:5000
Which acts as a smart contract (BPSC).
http://localhost:5001
One of the networks’ nodes that acts as a token sender.
http://localhost:5002
One of the networks’ nodes that acts as a token receiver.
To test, we first send two consecutive POST requests so that the node with port number 5001 is notified of its neighbor’s nodes on the network and stores the list of these nodes:
http://localhost:5001/nodes/register
It means, first “nodes”: [“http://127.0.0.1:5000"] as (BPSC) And then “nodes”: [“http://127.0.0.1:5002"] as another node is stored in the mentioned list.
Now we want to test the /smart/chain method. But before this test, we’d better send at least one transaction request and then a mining request to the (BPSC). This will save a chain with two blocks in (BPSC).
Now we send the following request, which is a GET type, to the address of the node that has port number 5001:
http://localhost:5001/smart/chain
By doing this, the node with port 5001 will receive the last chain from (BPSC). Thus, the node’s chain replaced with the latest chain created by (BPSC).
o in “smart blockchain”, although none of the nodes can produce blockchain, all of them can make their transactions through (BPSC) and receive the last chain from (BPSC). Also, as a decentralized network, nodes can communicate with each other, and even network nodes might profit by the services of dozens of (BPSCs) because it is assumed that each (BPSC) is dedicated to one type of token.
To make this seemingly complex subject, clear and transparent, we’d better implement a second chain for network nodes. Because by developing this pattern, you will be able to store dozens of parallel chains on each node. It’s simple, but it’s always good to drink more coffee. :)
import hashlib
import json
from time import time
from urllib.parse import urlparse
import requests
from flask import Flask, jsonify, request
class Smart_Blockchain:
def __init__(self):
self.current_information = []
self.chain = []
self.chain2 = []
self.nodes = set()
# Create the genesis block
self.new_block(previous_hash='1')
def register_node(self, address):
"""
Add a new node to the list of nodes
:param address: Address of node. Eg. 'http://192.168.0.5:5000'
"""
parsed_url = urlparse(address)
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 smart_chain(self):
"""
All nodes can receive the smart_chain
"""
schain = None
response = requests.get(f'http://127.0.0.1:5000/chain')
if response.status_code == 200:
chain = response.json()['chain']
schain = chain
# Replace our chain
if schain:
self.chain = schain
return True
return False
def new_block(self, previous_hash):
"""
Create a new Block in the Smart Blockchain
:param previous_hash: Hash of previous Block
:return: New Block
"""
block = {
'index2': len(self.chain2) + 1,
'timestamp': time(),
'information': self.current_information,
'previous_hash': previous_hash or self.hash(self.chain2[-1]),
}
# Reset the current list of transactions
self.current_information = []
self.chain2.append(block)
return block
def new_information(self, information):
"""
Creates a new information
:param information: Your information
:return: The index of the Block that will hold this information
"""
self.current_information.append({'information': information })
return self.last_block['index2'] + 1
@property
def last_block(self):
return self.chain2[-1]
@staticmethod
def hash(block):
"""
Creates a SHA-256 hash of a Block
:param block: Block
"""
# We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes
block_string = json.dumps(block, sort_keys=True).encode()
return hashlib.sha256(block_string).hexdigest()
# Instantiate the Node
app = Flask(__name__)
# Instantiate the Smart_Blockchain
blockchain = Smart_Blockchain()
@app.route('/mine', methods=['GET'])
def mine():
last_block = blockchain.last_block
# Forge the new Block by adding it to the chain
previous_hash = blockchain.hash(last_block)
block = blockchain.new_block(previous_hash)
response = {
'message': "New Block Forged",
'index2': block['index2'],
'information': block['information'],
'previous_hash': block['previous_hash'],
}
return jsonify(response), 200
@app.route('/information/new', methods=['POST'])
def new_information():
values = request.get_json()
# Check that the required fields are in the POST'ed data
required = ['information']
if not all(k in values for k in required):
return 'Missing values', 400
# Create a new information
index = blockchain.new_information(values['information'])
response = {'message': f'information will be added to Block {index}'}
return jsonify(response), 201
@app.route('/chain2', methods=['GET'])
def full_chain2():
response = {
'chain2': blockchain.chain2,
'length': len(blockchain.chain2),
}
return jsonify(response), 200
@app.route('/chain', methods=['GET'])
def full_chain():
response = {
'chain': blockchain.chain,
'length': len(blockchain.chain),
}
return jsonify(response), 200
@app.route('/nodes/register', methods=['POST'])
def register_nodes():
values = request.get_json()
nodes = values.get('nodes')
if nodes is None:
return "Error: Please supply a valid list of nodes", 400
for node in nodes:
blockchain.register_node(node)
response = {
'message': 'New nodes have been added',
'total_nodes': list(blockchain.nodes),
}
return jsonify(response), 201
@app.route('/smart/chain', methods=['GET'])
def smart_chain():
replaced = blockchain.smart_chain()
if replaced:
response = {
'message': 'Smart chain update by bpsc',
'smart chain': blockchain.chain,
'length': len(blockchain.chain)
}
else:
response = {
'message': 'Unsuccessful: Please try again',
'old chain': blockchain.chain,
'length': len(blockchain.chain)
}
return jsonify(response), 200
if __name__ == '__main__':
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('-p', '--port', default=5001, type=int, help='port to listen on')
args = parser.parse_args()
port = args.port
app.run(host='0.0.0.0', port=port)
If you take a closer look at the above code, two parallel and independent blockchains are stored on one node. Of course, one of them is the previous “smart blockchain” and the other is a simple blockchain that has been added to the previous code to store personal data. Of course, the second blockchain could be a “smart blockchain,” and a node could use dozens of (BPSC) services in the same way. (For example, the transaction of different tokens stored in independent blockchains.)
To create the second chain, two lists have been added to the previous lists. Line 12 is for storing data and Line 14 is for storing blockchain. You can check the rest of the modification yourself. The nodes_v2_5001.py and the nodes_v2_5002.py files are created exactly according to the above code. Please run these two files as well as the bpsc102.py file at the same time so that you can do the following tests.
Now, we want to test the /chain2 method. The nodes_v2_5001.py file represents a node that has port number 5001. We send a request for storing personal data to this node. Of course, we know that the initial block or Genesis Block is built at first, so the server responds that this data will be added to the second block. Then we send the mining request for the same node. This will create the next block (the second block on the second chain).
http://localhost:5001/information/new
http://localhost:5001/mine
We send the following request, which is of the GET type, to the same node:
http://localhost:5001/chain2
By doing this test, we make sure that the two blocks are in the second chain of our server, and the second chain has no relation with the first chain.
To make sure that the second chain does not interfere with the functionality of the “smart blockchain”, we repeat exactly the previous tests for the “smart blockchain”. That is, we first test the /nodes/register method for all three nodes. Then, from the node that plays the role of (BPSC), we request several transactions and multiple mining:
http://localhost:5000/transactions/new
http://localhost:5000/mine
Finally, we send the following request, which is of GET type, to the address of the node that has port number 5001:
http://localhost:5001/smart/chain
As you can see in the pictures, the last chain created by (BPSC) substituted for the main chain (first chain) of this node. And finally, we conclude that both the “smart blockchain” and the second chain stored in this node completely independent.
When a smart contract is registered onto the blockchain network, it becomes immortal. That is, as long as that blockchain network is working, no human (even the coder and owner of the smart contract) can make the slightest change in the initial content of the contract. Block producer smart contract (BPSC) has the same situation. So from the beginning, the time of mining operation (block production) should be clear and implemented in the codes. Otherwise, (BPSC) will always have to wait for the mining request to be able to mine the new block.
There are three ways to schedule a (BPSC) smart contract for mining operations(block producing):
A — based on time, build a new block, for example, every second or every minute.
B — based on the number of transactions; for example, after every ten transactions or one hundred transactions.
C — a combination of methods A and B; for example, ten seconds or ten transactions, each occurring sooner.
Of course, we only implement one example of the first method. For example, suppose that after each transaction, a new block is created by (BPSC). So each block contains only one transaction.
..........
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
values = request.get_json()
# Check that the required fields are in the POST'ed data
required = ['sender', 'amount', 'recipient']
if not all(k in values for k in required):
return 'Missing values', 400
# Create a new Transaction
index = blockchain.new_transaction(values['sender'], values['amount'], values['recipient'])
response = {'message': f'Transaction will be added to Block {index}'}
last_block = blockchain.last_block
# Forge the new Block by adding it to the chain
previous_hash = blockchain.hash(last_block)
block = blockchain.new_block(previous_hash)
response = {
'message': "New Block Forged",
'index': block['index'],
'transactions': block['transactions'],
'previous_hash': block['previous_hash'],
}
return jsonify(response), 201
..........
As you see, the modifications have only been made in the /transactions/new method, and exactly the codes of /mine have been added to the end of that method. We created the bpsc103.py file by applying the same changes. You can run this file and perform your desired tests using Postman.
In this case, if you send the following request to the server:
http://localhost:5000/transactions/new
(BPSC) immediately makes both the transaction and mining a block and adds it to the chain.
Now, if, for example, you decide to send the mining request to the server: http://localhost:5000/mine
only an empty block will be minded because no transaction is expected.
To simulate the (BPSC) block producer smart contract, we considered a node with a port number 5000 and implemented the following three files:
bpsc101.py This version is not final and cannot be interacted with other nodes.
bpsc102.py This is the final version and all cases have been tested for this version. We tested the transaction request, the mining request, the request to send the last chain for the other nodes, and so on.
bpsc103.py This version is also final, but in this version, we have added a new feature to the previous version. That is, (BPSC) required to create a new block after each transaction and add that block to its chain.
The ports 5001 and 5002 have the representation of other network nodes. To simulate these nodes, we implemented the following two versions:
nodes_v1_5001.py and nodes_v1_5002.py
This version is according to “Smart Blockchain” technology and final. Although these nodes cannot produce blocks, they can do their transactions through (BPSC) and also receive the last chain from (BPSC).
nodes_v2_5001.py and nodes_v2_5002.py
This is also final, but in this version, we have added a new feature to the previous version. It means a simple blockchain (To store personal data) has been added to the previous code.
The coding is over and you can download all the article files here. You can also do more tests through separate machines or with friends. But bearing in mind that Python simulation, whether for blockchain or “smart blockchain,” usually doesn’t include all the necessary controls and operations.
In this article, some controls were not performed; for example, the necessary controls for wallets were not performed. We also want to return all tokens to the sender if, for any reason, (BPSC) fails to send the tokens to the final recipient.
But it’s not implemented into codes either. This kind of control should not be neglected in real work. Most smart contracts are written in specific languages, so-called high-level. For example, smart contracts in Ethereum blockchain are usually written in Solidity language. Fortunately, implementing this type of control with Solidity is even easier than Python.
If you are interested, you can also read the following article. We have implemented exactly one "Smart Blockchain" with Solidity language:
In this paper and several previous ones, we have shown that miners, block producers, and their activities are not an integral part of blockchain technology. Of course, it is a new and controversial idea to replace miners and block producers with smart contracts, and like any big work, there are many objections and obstacles in the way, but maintaining the status quo is not possible in the long run.
For example, by increasing the volume of calculations in machine learning algorithms, we are witnessing an increase in the processing capacity of the machine at the same time. But in practice, the proof of work mechanism (POW) for Bitcoin is a real impasse.
All miners are increasing their processing capacity, but to compete with each other, they still have to consume the amount of electrical power that equals the annual consumption of a country with ten million population. This is not fair.
Anyway, if you are interested in the subject, you can read our previous articles on “smart blockchain” as well as bitcoin problems.
Also, if you have any questions or you see any ambiguities or errors in this article, please let us know.