Часть 2: с большим количеством строк Python
Примечание. В этой статье предполагается, что вы прочитали первую часть
Самый крошечный блокчейн был чрезвычайно прост, и его было относительно легко сделать. Но вместе с его простотой появилось и несколько недостатков. Во-первых, SnakeCoin работал только на одной машине, поэтому он был далек от распространения, не говоря уже о децентрализации. Во-вторых, блоки могут быть добавлены в цепочку так же быстро, как главный компьютер может создать объект Python и добавить его в список. В случае простого блокчейна это не проблема, но теперь мы собираемся позволить SnakeCoin быть реальной криптовалютой, поэтому нам нужно будет контролировать количество блоков (и монет), которые могут быть созданы одновременно.
Отныне данные SnakeCoin будут транзакциями, поэтому поле данных каждого блока будет представлять собой список некоторых транзакций. Мы определим транзакцию следующим образом. Каждая транзакция будет представлять собой объект JSON с подробным описанием отправителя монеты, получателя монеты и суммы переводимого SnakeCoin.
Примечание. Транзакции выполняются в формате JSON по причине, которую я скоро опишу.
"from": "71238uqirbfh894-random-public-key-a-alkjdflakjfewn204ij",
"to": "93j4ivnqiopvh43-random-public-key-b-qjrgvnoeirbnferinfo",
"amount": 3
}
Теперь, когда мы знаем, как будут выглядеть наши транзакции, нам нужен способ добавить их на один из компьютеров в нашей блокчейн сети, называемый узлом. Для этого мы создадим простой HTTP сервер, чтобы любой пользователь мог сообщить нашим узлам, что произошла новая транзакция. Узел сможет принять POST запрос с транзакцией (как показано выше) в качестве тела запроса. Вот почему транзакции в формате JSON; нам нужно, чтобы они передавались на наш сервер в теле запроса.
from flask import request
node = Flask(__name__)
# Store the transactions that
# this node has in a list
this_nodes_transactions = []
@node.route('/txion', methods=['POST'])
def transaction():
if request.method == 'POST':
# On each new POST request,
# we extract the transaction data
new_txion = request.get_json()
# Then we add the transaction to our list
this_nodes_transactions.append(new_txion)
# Because the transaction was successfully
# submitted, we log it to our console
print "New transaction"
print "FROM: {}".format(new_txion['from'])
print "TO: {}".format(new_txion['to'])
print "AMOUNT: {}\n".format(new_txion['amount'])
# Then we let the client know it worked out
return "Transaction submission successful\n"
node.run()
Потрясающе! Теперь у нас есть способ вести учет пользователей, когда они отправляют монеты друг другу. Вот почему люди называют блокчейны публичными распределенными бухгалтерскими книгами: все транзакции хранятся для всеобщего обозрения и хранятся на каждом узле в сети.
Но возникает вопрос: где люди могут получить SnakeCoins? Пока нигде. Еще нет такого понятия, как SnakeCoin, потому что еще не было создано и выпущено ни одной монеты. Чтобы создавать новые монеты, люди должны добывать (майнить) новые блоки SnakeCoin. Когда они успешно добывают новые блоки, создаются новые SnakeCoin и вознаграждается то, кто добыл блок. Затем монета получает распространение, как только майнер отправляет SnakeCoin другому человеку.
Мы не хотим, чтобы было слишком легко добывать новые блоки SnakeCoin, потому что это создаст слишком много SnakeCoin, и они будут иметь небольшую ценность. И наоборот, мы не хотим, чтобы было слишком сложно добывать новые блоки, потому что тогда не будет достаточно монет для всех, чтобы тратить их, и они будут слишком дорогими на наш вкус. Чтобы контролировать сложность майнинга новых SnakeCoins, мы реализуем алгоритм Proof-of-Work (PoW). Алгоритм Proof-of-Work - это по сути алгоритм, который генерирует элемент, который сложно создать, но легко проверить. Предмет называется доказательством и как звучит, является доказательством того, что компьютер выполнил определенный объем работы.
В SnakeCoin мы создадим несколько простой алгоритм Proof-of-Work. Чтобы создать новый блок, компьютер майнера должен будет увеличить число. Когда это число делится на 9 (количество букв в «SnakeCoin») и номер доказательства последнего блока, будет добыт новый блок SnakeCoin, и майнер получит новый SnakeCoin.
# ...Block class definition
miner_address = "q3nf394hjg-random-miner-address-34nf3i4nflkn3oi"
def proof_of_work(last_proof):
# Create a variable that we will use to find
# our next proof of work
incrementor = last_proof + 1
# Keep incrementing the incrementor until
# it's equal to a number divisible by 9
# and the proof of work of the previous
# block in the chain
while not (incrementor % 9 == 0 and incrementor % last_proof == 0):
incrementor += 1
# Once that number is found,
# we can return it as a proof
# of our work
return incrementor
@node.route('/mine', methods = ['GET'])
def mine():
# Get the last proof of work
last_block = blockchain[len(blockchain) - 1]
last_proof = last_block.data['proof-of-work']
# Find the proof of work for
# the current block being mined
# Note: The program will hang here until a new
# proof of work is found
proof = proof_of_work(last_proof)
# Once we find a valid proof of work,
# we know we can mine a block so
# we reward the miner by adding a transaction
this_nodes_transactions.append(
{ "from": "network", "to": miner_address, "amount": 1 }
)
# Now we can gather the data needed
# to create the new block
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
# Empty transaction list
this_nodes_transactions[:] = []
# Now create the
# new block!
mined_block = Block(
new_block_index,
new_block_timestamp,
new_block_data,
last_block_hash
)
blockchain.append(mined_block)
# Let the client know we mined a block
return json.dumps({
"index": new_block_index,
"timestamp": str(new_block_timestamp),
"data": new_block_data,
"hash": last_block_hash
}) + "\n"
Теперь мы можем контролировать количество блоков, добываемых за определенный период времени, и мы можем выпускать новые монеты для людей в сети для отправки друг другу. Но, как мы уже говорили, мы делаем это только на одном компьютере. Если блокчейны децентрализованы, как мы можем быть уверены, что одна и та же цепочка находится на каждом узле? Для этого мы заставляем каждый узел транслировать свою версию цепочки другим узлам и позволяем им принимать цепочки других узлов. После этого каждый узел должен проверить цепочки других узлов, чтобы каждый узел в сети мог прийти к консенсусу о том, как будет выглядеть результирующий блокчейн.. Это называется консенсусным алгоритмом.
Наш алгоритм консенсуса будет довольно простым: если цепочка одного узла отличается от другой (т.е. есть конфликт), то самая длинная цепочка в сети остается, а все более короткие цепочки будут удалены. Если нет конфликта между цепочками в нашей сети, то мы продолжаем.
def get_blocks():
chain_to_send = blockchain
# Convert our blocks into dictionaries
# so we can send them as json objects later
for block in chain_to_send:
block_index = str(block.index)
block_timestamp = str(block.timestamp)
block_data = str(block.data)
block_hash = block.hash
block = {
"index": block_index,
"timestamp": block_timestamp,
"data": block_data,
"hash": block_hash
}
# Send our chain to whomever requested it
chain_to_send = json.dumps(chain_to_send)
return chain_to_send
def find_new_chains():
# Get the blockchains of every
# other node
other_chains = []
for node_url in peer_nodes:
# Get their chains using a GET request
block = requests.get(node_url + "/blocks").content
# Convert the JSON object to a Python dictionary
block = json.loads(block)
# Add it to our list
other_chains.append(block)
return other_chains
def consensus():
# Get the blocks from other nodes
other_chains = find_new_chains()
# If our chain isn't longest,
# then we store the longest chain
longest_chain = blockchain
for chain in other_chains:
if len(longest_chain) < len(chain):
longest_chain = chain
# If the longest chain wasn't ours,
# then we set our chain to the longest
blockchain = longest_chain
Мы уже почти закончили. После запуска полного кода сервера SnakeCoin выполните следующие команды в своем терминале. При условии, что у вас установлен cURL.
1. Создать транзакцию.
-H "Content-Type: application/json" \
-d '{"from": "akjflw", "to":"fjlakdj", "amount": 3}'
2. Майнить новый блок.
3. Проверьте результаты. В окне клиента мы видим это.
С небольшим количеством красивой печати мы видим, что после майнинга мы получаем некоторую крутую информацию о нашем новом блоке.
"index": 2,
"data": {
"transactions": [
{
"to": "fjlakdj",
"amount": 3,
"from": "akjflw"
},
{
"to": "q3nf394hjg-random-miner-address-34nf3i4nflkn3oi",
"amount": 1,
"from": "network"
}
],
"proof-of-work": 36
},
"hash": "151edd3ef6af2e7eb8272245cb8ea91b4ecfc3e60af22d8518ef0bba8b4a6b18",
"timestamp": "2017-07-23 11:23:10.140996"
}
Вот и все! На данный момент мы сделали блокчейн довольно большого размера. Теперь SnakeCoin может быть запущен на нескольких машинах для создания сети, а реальные SnakeCoin могут быть добыты. Пожалуйста, не стесняйтесь возиться с кодом сервера SnakeCoin столько раз, сколько захотите, и задавайте столько вопросов, сколько вам нужно! В следующей части мы обсудим создание кошелька SnakeCoin, чтобы пользователи могли отправлять, получать и хранить свои SnakeCoin.
Большое спасибо за чтение!
Twitter , Github , Snapchat , Instagram
Перевод.
Оригинал: https://medium.com/crypto-currently/lets-make-the-tiniest-blockchain-bigger-ac360a328f4d