Блокчейн python: Блокчейн на Python / Хабр

Содержание

Блокчейн на Python / Хабр

Когда я читал статью про

блокчейн на JavaScript

, мне было интересно познакомиться с идеями о блокчейн-разработке, которые отличаются от тех, что мне уже известны. А как только я начал читать код, мне захотелось сопоставить его с аналогичным Python-кодом, чтобы ещё и разобраться с его отличиями от кода, написанного на JavaScript.

Цель этого материала заключается в том, чтобы выявить отличия языков. Его можно считать Python-дополнением к исходной статье.

Несмотря на то, что исходная статья появилась на свет после того, как её автор ознакомился с примером блокчейн-разработки на Python, мне хотелось написать Python-код, который как можно более точно воспроизводит JavaScript-код из статьи. Это позволит сопоставить реализацию блокчейна на разных языках.

Я, кроме того, собираюсь сделать так, чтобы моя реализация блокчейна, как и в статье про JavaScript, тоже поместилась бы в 60 строк.

О блокчейне

Хотя я собирался повторить структуру того материала, чтобы тем же путём, что и его автор, прийти к готовому коду, я, всё же, включу сюда и кое-что своё. В частности, я предпочитаю другое определение блокчейна. Оно звучит так: «Блокчейн — это система регистрации информации, выполняемой таким способом, который усложняет или делает невозможным изменение информации, взлом системы или мошенничество с информацией».

Подготовка среды разработки

В этом проекте мы будем использовать Python, поэтому, если он у вас не установлен — найдите дистрибутив, подходящий для вашей ОС, и установите его.

Создание блока

Блок — это объект, в котором имеется некая информация. Поэтому начнём работу с создания класса

Block

:

class Block:

    def __init__(self, timestamp=None, data=None):
        self.timestamp = timestamp or time()
        # В this.data должна храниться информация, вроде сведений о транзакциях.
        self.data = [] if data is None else data

Определения класса

Block

в Python и JavaScript получились очень похожими. В Python вместо

this

используется

self

, а аналогом метода

constructor

является

init

.

Комментарии тоже выполняются похожим образом. В Python для оформления однострочных комментариев применяется символ #, а в JavaScript — конструкция //.

Реализацию алгоритма sha256 я взял из библиотеки hashlib. В JS-проекте она берётся из пакета

crypto.

from hashlib import sha256

class Block:

    def __init__(self, timestamp=None, data=None):
        self.timestamp = timestamp or time()
        self.data = [] if data is None else data
        self.hash = self.getHash()
        self.prevHash = None # Хеш предыдущего блока

    def getHash(self):
        hash = sha256()
        hash.update(str(self.prevHash).encode('utf-8'))
        hash.update(str(self.timestamp).encode('utf-8'))
        hash.update(str(self.data).encode('utf-8'))
        return hash.hexdigest()

В методе

getHash

всё начинается с пустого хеша, который мы формируем с использованием данных, хранящихся в блоке.

Хеш вычисляется на основе хеша предыдущего блока, отметки времени и данных, хранящихся в блоке. Всё это преобразуется в последовательности байтов с помощью метода

.encode('utf-8')

.

Блокчейн

Займёмся классом

Blockchain

.

class Blockchain:
    def __init__(self):
        # В этом свойстве будут содержаться все блоки.
        self.chain = []

Классы, представляющие собой блокчейн, похожи в обоих языках.

Для того чтобы создать первичный блок, мы просто вызываем Block с текущей отметкой времени, для получения которой используем

time(). Для этого нам нужно импортировать библиотеку time.

Преобразование числа в строку выполняется в Python с помощью функции str(), а не с помощью метода toString(), как делается в JavaScript.

from time import time

class Blockchain:
    def __init__(self):
        # Создаём первичный блок
        self. chain = [Block(str(int(time())))]

Похоже выглядит и метод для получения самого свежего блока. Только тут, в отличие от JavaScript-проекта, для выяснения длины цепочки блоков, вместо свойства

length

используется функция

len()

.

    def getLastBlock(self):
        return self.chain[len(self.chain) - 1]

Для того чтобы добавить блок в блокчейн, мы просто вызываем метод

addBlock

. Код получился почти таким же, как в JS-проекте, только тут, вместо метода

push()

, используется метод

append()

.

def addBlock(self, block):
        # Так как мы добавляем новый блок, prevHash будет хешем предыдущего последнего блока.
        block.prevHash = self.getLastBlock().hash
        # Так как теперь в prevHash имеется значение, мы должны пересчитать хеш блока.
        block.hash = block.getHash()
        self.chain.append(block)

Проверка блокчейна

В методе, используемом для проверки блокчейна, мы пользуемся функцией

range()

. В этом — серьёзное отличие нашего кода от кода JS-проекта. И, кроме того, так как мы в Python не пользуемся константами, тут мы просто применяем обычные переменные.

При проверке условий в Python, вместо ||, используется or.

def isValid(self):
    # Перед перебором цепочки блоков нужно установить i в 1, так как до первичного блока никаких блоков нет. В результате мы начинаем со второго блока.
    for i in range(1, len(self.chain)):
        currentBlock = self.chain[i]
        prevBlock = self.chain[i - 1]

        # Проверка
        if (currentBlock.hash != currentBlock.getHash() or prevBlock hash != currentBlock.prevHash):
            return False

    return True

Алгоритм доказательства выполнения работы

Реализовать алгоритм доказательства выполнения работы мы можем, начав с добавления в класс

Block

метода

mine()

и свойства

nonce

. Тут стоит проявить внимательность, так как свойство

nonce

должно быть объявлено до вызова метода

self. getHash()

. В противном случае будет выдана ошибка

AttributeError: 'Block' object has no attribute 'nonce'

.

class Block:

    def __init__(self, timestamp=None, data=None):
        self.timestamp = timestamp or time()
        self.data = [] if data is None else data
        self.prevHash = None # хеш предыдущего блока
        self.nonce = 0
        self.hash = self.getHash()

    # Наша хеш-функция.
    def getHash(self):

        hash = sha256()
        hash.update(str(self.prevHash).encode('utf-8'))
        hash.update(str(self.timestamp).encode('utf-8'))
        hash.update(str(self.data).encode('utf-8'))
        hash.update(str(self.nonce).encode('utf-8'))
        return hash.hexdigest()

    def mine(self, difficulty):
        # Тут запускается цикл, работающий до тех пор, пока хеш не будет начинаться со строки
        # 0...000 длины <difficulty>.
        while self.hash[:difficulty] != '0' * difficulty:
            # Инкрементируем nonce, что позволяет получить совершенно новый хеш.
            self.nonce += 1             # Пересчитываем хеш блока с учётом нового значения nonce.             self.hash = self.getHash()

Создадим свойство, в котором будет храниться сложность:

self.difficulty = 1

Отредактируем метод

addBlock

:

    def addBlock(self, block):
        block.prevHash = self.getLastBlock().hash
        block.hash = block.getHash()
        block.mine(self.difficulty)
        self.chain.append(block)

Тестирование блокчейна

Импортируем модуль и воспользуемся классом

Blockchain

так же, как таким же классом в JS-проекте:

from blockchain import Block
from blockchain import Blockchain
from time import time

JeChain = Blockchain()

# Добавим новый блок
JeChain.addBlock(Block(str(int(time())), ({"from": "John", "to": "Bob", "amount": 100})))
# (Это - всего лишь интересный эксперимент, для создания настоящей криптовалюты обычно нужно сделать намного больше, чем сделали мы).
# Вывод обновлённого блокчейна print(JeChain)

Результаты работы этого кода должны выглядеть примерно так:

[
    {
        "data": [],
        "timestamp": "1636153236",
        "nonce": 0,
        "hash": "4caa5f684eb3871cb0eea217a6d043896b3775f047e699d92bd29d0285541678",
        "prevHash": null
    },
    {
        "data": {
            "from": "John",
            "to": "Bob",
            "amount": 100
        },
        "timestamp": "1636153236",
        "nonce": 14,
        "hash": "038f82c6e6605acfcad4ade04e454eaa1cfa3d17f8c2980f1ee474eefb9613e9",
        "prevHash": "4caa5f684eb3871cb0eea217a6d043896b3775f047e699d92bd29d0285541678"
    }
]

Но заработает это всё только после добавления в класс

Blockchain

метода

__repr__()

:

import json

    def __repr__(self):
        return json.dumps([{'data': item.data, 'timestamp': item.timestamp, 'nonce': item.nonce, 'hash': item.hash, 'prevHash': item. prevHash} for item in self.chain], indent=4)

Дополнение: сложность и время блока

Для настройки времени блока нам понадобится соответствующее свойство:

self.blockTime = 30000

Посмотрим на тернарный оператор, используемый в системе настройки сложности. Если переписать JS-конструкцию с тернарным оператором на Python, то получится следующее:

(if_test_is_false, if_test_is_true)[test]

:

def addBlock(self, block):
        block.prevHash = self.getLastBlock().hash
        block.hash = block.getHash()
        block.mine(self.difficulty)
        self.chain.append(block)

        self.difficulty += (-1, 1)[int(time()) - int(self.getLastBlock().timestamp) < self.blockTime]

Итоговый Python-код (без нормального форматирования) укладывается в обещанные 60 строк:

# -*- coding: utf-8 -*-

from hashlib import sha256
import json
from time import time

class Block:

    def __init__(self, timestamp=None, data=None):
        self. timestamp = timestamp or time()
        self.data = [] if data is None else data
        self.prevHash = None
        self.nonce = 0
        self.hash = self.getHash()

    def getHash(self):

        hash = sha256()
        hash.update(str(self.prevHash).encode('utf-8'))
        hash.update(str(self.timestamp).encode('utf-8'))
        hash.update(str(self.data).encode('utf-8'))
        hash.update(str(self.nonce).encode('utf-8'))
        return hash.hexdigest()

    def mine(self, difficulty):
        while self.hash[:difficulty] != '0' * difficulty:
            self.nonce += 1
            self.hash = self.getHash()

class Blockchain:

    def __init__(self):
        self.chain = [Block(str(int(time())))]
        self.difficulty = 1
        self.blockTime = 30000

    def getLastBlock(self):
        return self.chain[len(self.chain) - 1]

    def addBlock(self, block):
        block.prevHash = self.getLastBlock().hash
        block.hash = block.getHash()
        block.mine(self.difficulty)
        self. chain.append(block)

        self.difficulty += (-1, 1)[int(time()) - int(self.getLastBlock().timestamp) < self.blockTime]

    def isValid(self):
        for i in range(1, len(self.chain)):
            currentBlock = self.chain[i]
            prevBlock = self.chain[i - 1]

            if (currentBlock.hash != currentBlock.getHash() or prevBlock.hash != currentBlock.prevHash):
                return False

        return True

    def __repr__(self):
        return json.dumps([{'data': item.data, 'timestamp': item.timestamp, 'nonce': item.nonce, 'hash': item.hash, 'prevHash': item.prevHash} for item in self.chain], indent=4)

Надеюсь, вам понравились оба материала, и вы нашли в них что-то полезное.

Если бы вам понадобилось создать блокчейн-систему — какими инструментами вы воспользовались бы?

Строим собственный блокчейн на Python и разбираемся в его особенностях

Перед тем как начать строить блокчейн, необходимо понять его основы.

Блокчейн  —  это технология, используемая для записи и хранения данных. Например, он может содержать информацию о переводах, которые проводятся в любом банке, а также права собственности, соглашения, личные сообщения и другие данные.

Его главная особенность заключается в том, что хранение данных не происходит в одном центральном органе. Благодаря этому не получится сфальсифицировать данные, взломав только один главный пункт. Самое известное применение блокчейна  —  это Bitcoin, один из видов криптовалют.

Углубленное изучение блокчейна может вызвать трудности, а практика лучше всего помогает упростить этот процесс. Это руководство рассчитано на новичков с небольшими знаниями Python.

Требования

Убедитесь, что у вас установлена самая последняя версия Python, а также две его библиотеки  —  Flask и Requests.

pip install Flask request

Для создания запросов рекомендуем скачать Postman.

Начало

Создадим новый файл в Python и назовём его app.py. Сначала нам нужно написать класс Blockchain:

class Blockchain(object):

После создания конструктора добавляем пустой список для хранения блокчейна:

def __init__(self): 
 
  self. chain = []

Ещё нам понадобится список для хранения транзакций, поэтому получаем вот такой класс Blockchain:

class Blockchain(object):
    def __init__(self):
       self.chain = []
       self.current_transactions = []

Для внесения данных в эти списки создадим два метода: addBlock() и addTransaction().

def addBlock(self):
    pass

def addTransaction(self):
    pass

Пока эти функции не будут выполнять никаких действий, но скоро мы к ним вернёмся.

Далее хешируем данные, чтобы обезопасить их. Создадим метод hash(). Поскольку он статический, не забудьте добавить @staticmethod:

@staticmethod
    def hash(block):
        pass

И наконец напишем метод lastBlock():

@property
    def lastBlock(self):
        pass

Создание блока

Теперь подумаем над тем, как будет выглядеть блок. В первую очередь для него нужен индекс index и временной штамп timestamp. А как же Proof? Что это? К этому мы вернёмся позже.

Также необходимо добавить список транзакций transactions и хеш предыдущего блока previous_hash.

Внесение новых транзакций

Новые транзакции создаём с помощью метода addTransaction(). Теперь заполним его кодом.

Сначала введём несколько аргументов: self, sender, recipient, amount.

def new_transaction(self, sender, recipient, amount):

Далее добавим новую транзакцию в список:

self.current_transactions.append({
            ‘sender’: sender,
            ‘recipient’: recipient,
            ‘amount’: amount,
          })

В конце этот метод должен возвращать индекс транзакции:

return self.last_block[‘index’] + 1

Что придаёт ему такой вид:

def addTransaction(self, sender, recipient, amount):
        self. current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })

        return self.last_block['index'] + 1

Создание нового блока

Прежде чем начать строить новые блоки, нужно создать в конструкторе один центральный блок:

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

Затем заполняем метод addBlock():

def addBlock(self, proof, previous_hash=None):
        block = {
            'index': len(self.chain) + 1,
            'timestamp': time(),
            'transactions': self.current_transactions,
            'proof': proof,
            'previous_hash': previous_hash or self.hash(self.chain[-1]),
        }

        self.current_transactions = []

        self.chain.append(block)
        return block

Как вы могли заметить, нужно создать метод hash(), чтобы код заработал.

@staticmethod
    def hash(block):
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()

Использовать хеширование довольно легко. По сути, это шифрование строки.

Дальше нам нужно добавить ещё один метод  —  lastBlock():

@property
    def last_block(self):
        return self.chain[-1]

Что такое Proof

Proof of Work (доказательство выполнения работы)  —  это алгоритм, основная цель которого препятствовать кибератакам, например DDoS-атаке. Впервые идея о Proof of Work (PoW) была опубликована Синтией Дворк и Мони Наором в 1993 году.

Такой алгоритм необходим для интенсивных вычислений (также называемых «майнинг»), благодаря которым ненадёжные транзакции отсекаются в отдельную группу в блокчейне.

Чтобы подтвердить надёжность таких транзакций, майнеры должны решить математическую задачу. И тот, кто решит первым, получает вознаграждение (в виде новой криптовалюты). С каждым новым блоком задачи становятся чуть сложнее. Поэтому майнерам приходится работать эффективнее. Подтверждённые же транзакции хранятся в публичном блокчейне.

Применение PoW

Теперь реализуем Proof of Work для нашего блокчейна со следующей задачей:

Найти число х. Когда оно хешируется предыдущим решением блока, создаётся хеш с 2 заглавными нулями.

Чтобы её внести, добавим следующие модули:

import hashlib
import json
 
from time import time
from uuid import uuid4

Напишем два новых методов:

def proof_of_work(self, last_proof):

И:

@staticmethod
def valid_proof(last_proof, proof):

Заполним их вот так:

def proof_of_work(self, last_proof):
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        return proof

    @staticmethod
    def valid_proof(last_proof, proof):

        guess = f'{last_proof}{proof}'. encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        return guess_hash[:4] == "0000"

Применение Flask для API

Конечно, строить блокчейн интересно, но теперь пришло время его использовать. И у Python есть прекрасный модуль Flask, чтобы создать API. Добавим немного базового кода Flask:

from flask import Flask
app = Flask(__name__)
node_identifier = str(uuid4()).replace('-', '')
blockchain = Blockchain()
@app.route('/mine', methods=['GET'])
def mine():
    return "Mining a new Block"
  
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
    return "Adding a new Transaction"
@app.route('/chain', methods=['GET'])
def full_chain():
    response = {
        'chain': blockchain.chain,
        'length': len(blockchain.chain),
    }
    return jsonify(response), 200
if __name__ == '__main__':
    app. run(host='0.0.0.0', port=5000)

Подробнее о Flask можно узнать из его документации.

Новые транзакции

Конечная точка новых транзакций для API будет выглядеть таким образом. Довольно легко, ведь мы уже писали подобный метод:

@app.route('/transactions/new', methods=['POST'])
def newTransaction():
    values = request.get_json()
required = ['sender', 'recipient', 'amount']
    if not all(k in values for k in required):
        return 'Missing values', 400
index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])
response = {'message': f'Transaction will be added to Block {index}'}
    return jsonify(response), 201

Конечная точка майнинга

Здесь нам нужно добавить немного кода перед тем как заработает точка майнинга. И должно произойти лишь:

· доказательная работа;

· вознаграждения майнера;

· новый блок + добавление его к блокчейну.

@app.route('/mine', methods=['GET'])
    def mine():
        last_block = blockchain.last_block
        last_proof = last_block['proof']
        proof = blockchain.proof_of_work(last_proof)
        blockchain.new_transaction(
            sender="0",
            recipient=node_identifier,
            amount=1,
        )
        previous_hash = blockchain.hash(last_block)
        block = blockchain.new_block(proof, previous_hash)
response = {
            'message': "New Block Created",
            'index': block['index'],
            'transactions': block['transactions'],
            'proof': block['proof'],
            'previous_hash': block['previous_hash'],
        }
        return jsonify(response), 200

Запуск API блокчейна

Запустим API, открыв программу Python:

$ python app.py
$ Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Для майнинга блока можно использовать Postman или Curl:

$ curl -X POST -H "Content-Type: application/json" -d '{
 "sender": "d4ee26eee15148ee92c6cd394edd974e",
 "recipient": "address2",
 "amount": 5
}' "http://localhost:5000/transactions/new"

А узнать количество замайненных блоков можно на http://localhost:5000/chain. Появится цепь в формате JSON:

{
  "chain": [
    {
      "index": 1,
      "previous_hash": 1,
      "proof": 100,
      "timestamp": 1606910340,
      "transactions": []
    },
}

Полный код
mport requests
from time import time
import hashlib
import json
from flask import Flask
from time import time
from uuid import uuid4


class Blockchain(object):
    app = Flask(__name__)
    node_identifier = str(uuid4()).replace('-', '')

    blockchain = Blockchain()

    @app.route('/mine', methods=['GET'])
    def mine():
  
        last_block = blockchain.last_block
        last_proof = last_block['proof']
        proof = blockchain.proof_of_work(last_proof)

    
        blockchain.new_transaction(
            sender="0",
            recipient=node_identifier,
            amount=1,
        )

        previous_hash = blockchain. hash(last_block)
        block = blockchain.new_block(proof, previous_hash)

        response = {
            'message': "New Block Forged",
            'index': block['index'],
            'transactions': block['transactions'],
            'proof': block['proof'],
            'previous_hash': block['previous_hash'],
        }
        return jsonify(response), 200

    @app.route('/transactions/new', methods=['POST'])
    def newTransaction():
        values = request.get_json()

        required = ['sender', 'recipient', 'amount']
        if not all(k in values for k in required):
            return 'Missing values', 400


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

        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__':
        app.run(host='0.0.0.0', port=5000)

    def __init__(self):
        self.chain = []
        self.current_transactions = []

    def proof_of_work(self, last_proof):
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        return proof

    @staticmethod
    def valid_proof(last_proof, proof):

        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        return guess_hash[:4] == "0000"

    def addBlock(self, proof, previous_hash=None):
        block = {
            'index': len(self.chain) + 1,
            'timestamp': time(),
            'transactions': self. current_transactions,
            'proof': proof,
            'previous_hash': previous_hash or self.hash(self.chain[-1]),
        }

        self.current_transactions = []

        self.chain.append(block)
        return block

    def addTransaction(self, sender, recipient, amount):
        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })

        return self.last_block['index'] + 1

    @staticmethod
    def hash(block):
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()

    @property
    def last_block(self):
        return self.chain[-1]

Заключение

На этом всё. Вот как вы можете создать простой блокчейн на Python!

Читайте также:

Читайте нас в Telegram, VK и Яндекс. Дзен


Перевод статьи Bryan Dijkhuizen: Understand Blockchains by Building Your Own in Python

Создайте блокчейн с нуля с помощью Python

Автор считает, что самый быстрый способ изучить блокчейн — это создать его самостоятельно. В этой статье следует, что автор создает блокчейн с использованием Python.

Мы новички в развитии цифровой валюты и хотим знать, как реализована технология, лежащая в основе этого — блокчейн.

Но полностью понять блокчейн непросто. Мне нравится учиться на практике. Изучение технологий путем написания кода сделает его более надежным. Вы можете углубить свое понимание блокчейна, построив блокчейн.

Готов к работеЭта статья требует, чтобы читатели имели базовые представления о Python, уметь читать и писать на базовом языке Python и иметь базовое понимание HTTP-запросов.

Мы знаем, что блокчейн — это неизменная и упорядоченная цепная структура, состоящая из записей блоков. Записи могут быть транзакциями, файлами или любыми данными, которые вам нужны. Важно то, что они связаны хешами. оф.

Если вы не разбираетесь в хешировании, можете прочитать эту статью.

Экологическая подготовкаПодготовка среды, убедитесь, что вы установили Python3.6 +, pip, Flask, метод установки запросов:

pip install Flask==0.12.2 requests==2.18.4

Ему также нужен HTTP-клиент, такой как Postman, curl или другие клиенты.

Обратитесь к исходному коду (исходный код не запускается, когда я его перевожу, я создаю копию, исправляю ошибку и добавляю перевод, спасибо, звезда)

Начать создавать блокчейнСоздайте новый файл blockchain.py, все коды в этой статье написаны в этом файле, вы можете в любой момент обратиться к исходному коду.

Класс блокчейнаСначала создайте класс Blockchain и создайте два списка в конструкторе: один для хранения цепочки блоков, а другой — для хранения транзакций.

Ниже приводится структура класса Blockchain:

class Blockchain(object):
def __init__(self):
self. chain = []
self.current_transactions = []
def new_block(self):
# Creates a new Block and adds it to the chain
pass
def new_transaction(self):
# Adds a new transaction to the list of transactions
pass
@staticmethod
def hash(block):
# Hashes a Block
pass
@property
def last_block(self):
# Returns the last Block in the chain
pass

Класс Blockchain используется для управления цепочкой: он может хранить транзакции, добавлять новые блоки и т. Д. Давайте еще больше улучшим эти методы.

Структура блокаКаждый блок содержит атрибуты: индекс (индекс), временную метку Unix (временную метку), список транзакций (транзакции), доказательство работы (поясняется позже) и хеш-значение предыдущего блока.

Ниже приводится структура блока:

block = {
'index': 1,
'timestamp': 1506057125.900785,
'transactions': [
{
'sender': "8527147fe1f5426f9dd545de4b27ee00",
'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",
'amount': 5,
}
],
'proof': 324984774000,
'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"}

На данный момент концепция блокчейна ясна. Каждый новый блок содержит хеш предыдущего блока. Это ключевой момент. Он гарантирует неизменность блокчейна. Хочу

Если вы хотите узнать, вы можете добавить QUN491308659, код: Cauchy, код: Cauchy. Если злоумышленник уничтожит предыдущий блок, хеш-код всех последующих блоков станет неверным. Если вы не понимаете, переваривайте его медленно и обратитесь к принципу учета на блокчейне.

Присоединяйтесь к сделкеЗатем нам нужно добавить транзакцию для завершения метода new_transaction

class Blockchain(object):
...
def new_transaction(self, sender, recipient, amount):
"""
 Сгенерируйте новую информацию о транзакции, и информация будет добавлена ​​в следующий блок, который будет добыт: param sender: <str> Адрес отправителя: param recipient: <str> Адрес Получателя: param amount: <int> Amount: return: <int> Индекс блока, который будет содержать эту транзакцию "" "
self.current_transactions.append({
'sender': sender,
'recipient': recipient,
'amount': amount,
})
return self. last_block['index'] + 1

Метод добавляет запись транзакции в список и возвращает индекс блока (следующего блока, который будет добыт), в который будет добавлена ​​запись, что будет полезно, когда пользователь отправит транзакцию позже.

Создать новый блокКогда создается экземпляр блокчейна, нам нужно построить генезис-блок (первый блок без предыдущего) и добавить к нему подтверждение работы. Каждый блок требует доказательства работы, обычно известного как майнинг, который будет объяснен позже.

Чтобы построить генезисный блок, нам также необходимо улучшить методы newblock (), newtransaction () и hash ():

import hashlibimport jsonfrom time import timeclass Blockchain(object):
def __init__(self):
self.current_transactions = []
self.chain = []
# Create the genesis block
self.new_block(previous_hash=1, proof=100)
def new_block(self, proof, previous_hash=None):
"""
 Сгенерируйте новый блок: param proof: <int> Доказательство, данное алгоритмом Proof of Work: param previous_hash: (Необязательно) <str> Хеш предыдущего блока: return: <dict> New Block
"""
block = {
'index': len(self. chain) + 1,
'timestamp': time(),
'transactions': self.current_transactions,
'proof': proof,
'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, recipient, amount):
"""
 Сгенерируйте новую информацию о транзакции, и эта информация будет добавлена ​​в следующий блок, который будет добыт: param sender: <str> Адрес отправителя: param recipient: <str> Адрес Получателя: param amount: <int> Amount: return: <int> Индекс блока, который будет содержать эту транзакцию "" "
self.current_transactions.append({
'sender': sender,
'recipient': recipient,
'amount': amount,
})
return self.last_block['index'] + 1
@property
def last_block(self):
return self.chain[-1]
@staticmethod
def hash(block):
"""
 Хеш-значение SHA-256 сгенерированного блока: param block: <dict> Block: return: <str>
"""
# 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()

С помощью приведенного выше кода и комментариев вы можете получить интуитивное представление о блокчейне.Далее давайте посмотрим, как вырывается блок.

Понять доказательство работыНовый блок строится на основе алгоритма доказательства работы (PoW). Цель PoW — найти число, удовлетворяющее определенным условиям. Это число сложно вычислить, но легко проверить. Это основная идея доказательства работы.

Чтобы облегчить понимание, приведите пример:

Предположим, что значение хэша произведения целого числа x, умноженного на другое целое число y, должно оканчиваться на 0, то есть hash (x * y) = ac23dc… 0. Установить переменную x = 5, найти значение y?

Реализация на Python следующая:

from hashlib import sha256
x = 5y = 0 # yunknownwhile sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0":
y += 1print(f'The solution is y = {y}')

Результат y = 21. Потому что:

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

В Биткойне используется алгоритм доказательства работы под названием Hashcash, который очень похож на вышеуказанную проблему. Майнеры соревнуются за подсчет результатов, чтобы побороться за право создавать блоки. Как правило, сложность вычисления прямо пропорциональна количеству определенных символов, которым должна соответствовать целевая строка. После того, как майнер вычислит результат, он получит вознаграждение в биткойнах. Конечно, этот результат очень легко проверить в Интернете.

Получите доказательство работыДавайте реализуем аналогичный алгоритм PoW.Правило: ищите такое число p, чтобы хэш-значение строки, объединенной с доказательством предыдущего блока, начиналось с 4 нулей.

import hashlibimport jsonfrom time import timefrom uuid import uuid4class Blockchain(object):
...
def proof_of_work(self, last_proof):
"""
 Простое доказательство работы:
 -Найти p, такой, что хэш (pp ') начинается с 4 нулей-p - это доказательство предыдущего блока, p'- текущее доказательство: param last_proof: <int>
:return: <int>
"""
proof = 0
while self. valid_proof(last_proof, proof) is False:
proof += 1
return proof
@staticmethod
def valid_proof(last_proof, proof):
"""
 Подтверждение проверки: начинается ли хэш (last_proof, proof) с 4 нулей?
:param last_proof: <int> Previous Proof :param proof: <int> Current Proof :return: <bool> True if correct, False if not.
"""
guess = f'{last_proof}{proof}'.encode()
guess_hash = hashlib.sha256(guess).hexdigest()
return guess_hash[:4] == "0000"

Способ измерить сложность алгоритма — изменить число, начинающееся с нуля. Используйте 4 для демонстрации, и вы обнаружите, что еще один ноль значительно увеличит время, необходимое для вычисления результата.

Теперь, когда класс Blockchain в основном завершен, используйте HTTP-запросы для взаимодействия.

Блокчейн как интерфейс APIМы будем использовать фреймворк Python Flask, который представляет собой облегченный фреймворк веб-приложений, который упрощает сопоставление сетевых запросов с функциями Python. Теперь мы позволим блокчейну работать в сети Flask.

Мы создадим три интерфейса:

  • / transaction / new Создать транзакцию и добавить ее в блок

  • / mine говорит серверу добывать новые блоки

  • / chain возвращает всю цепочку блоков

Создать узелНаш «Flask server» будет действовать как узел в сети блокчейн. Давайте сначала добавим код фреймворка:

import hashlibimport jsonfrom textwrap import dedentfrom time import timefrom uuid import uuid4from flask import Flaskclass Blockchain(object): ...# Instantiate our Nodeapp = Flask(__name__)# Generate a globally unique address for this nodenode_identifier = str(uuid4()).replace('-', '')# Instantiate the Blockchainblockchain = Blockchain()@app.route('/mine', methods=['GET'])def mine(): return "We'll mine a new Block"@app.route('/transactions/new', methods=['POST'])def new_transaction(): return "We'll add a new transaction"@app.route('/chain', methods=['GET'])def full_chain(): response = { 'chain': blockchain. chain, 'length': len(blockchain.chain), } return jsonify(response), 200if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)

Кратко объясните приведенный выше код: Строка 15: Создайте узел. Строка 18: Создайте случайное имя для узла. Строка 21: Класс Instance Blockchain. Строки 24–26: Создайте интерфейс / mine GET. Строки 28–30: создание интерфейса POST / transaction / new, который может отправлять данные транзакции в интерфейс Строки 32–38: создание интерфейса / chain и возврат всей цепочки блоков. Строки 40-41: служба работает на порту 5000.

Отправить транзакциюСтруктура данных транзакции, отправляемая на узел, выглядит следующим образом:

{ "sender": "my address", "recipient": "someone else's address", "amount": 5}

Раньше был способ добавления транзакций, добавлять транзакции на основе интерфейса очень просто.

import hashlibimport jsonfrom textwrap import dedentfrom time import timefrom uuid import uuid4from flask import Flaskclass Blockchain(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'])def mine():
return "We'll mine a new Block"@app.route('/transactions/new', methods=['POST'])def new_transaction():
return "We'll add a new transaction"@app.route('/chain', methods=['GET'])def full_chain():
response = {
'chain': blockchain.chain,
'length': len(blockchain.chain),
}
return jsonify(response), 200if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

Добыча полезных ископаемыхМайнинг — это волшебство. Он очень простой. Он выполняет три функции:

  1. PoW для расчета Proof of Work

  2. Предоставьте майнеру (себе) монету, добавив транзакцию

  3. Создайте новый блок и добавьте его в цепочку

import hashlibimport jsonfrom textwrap import dedentfrom time import timefrom uuid import uuid4from flask import Flask, jsonify, [email protected]('/transactions/new', methods=['POST'])def new_transaction():
values = request. get_json()
# Check that the required fields are in the POST'ed data
required = ['sender', 'recipient', 'amount']
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['recipient'], values['amount'])
response = {'message': f'Transaction will be added to Block {index}'}
return jsonify(response), 201

Обратите внимание, что получателем транзакции является наш собственный серверный узел, и большая часть работы, которую мы делаем, просто взаимодействует с методами класса Blockchain. На этом этапе, даже если наша цепочка блоков завершена, давайте запустим ее.

Запустить блокчейнВы можете использовать cURL или Postman для взаимодействия с API

Запускаем сервер:

$ python blockchain.py* Runing on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Давайте начнем, запросив http: // localhost: 5000 / mine

Запросить майнинг с помощью Postman

Добавить новую транзакцию через почтовый запрос

Запросить майнинг с помощью Postman

Если вы не используете Postman, следующий оператор cURL будет таким же:

$ curl -X POST -H "Content-Type: application/json" -d '{
"sender": "d4ee26eee15148ee92c6cd394edd974e",
"recipient": "someone-other-address",
"amount": 5}' "http://localhost:5000/transactions/new"

После двойного рытья есть 3 блока, и всю информацию о блоке можно получить, запросив 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}

Последовательность (консенсус)У нас уже есть базовый блокчейн, который может принимать транзакции и майнинг. Но система блокчейн должна быть распределенной. Поскольку он распределен, как мы можем гарантировать, что все узлы имеют одну и ту же цепочку? Это проблема согласованности. Если мы хотим иметь несколько узлов в сети, мы должны реализовать согласованный алгоритм.

Зарегистрировать узелПеред реализацией алгоритма консенсуса нам нужно найти способ сообщить узлу о своих соседях. Каждый узел должен вести учет других узлов в сети. Итак, давайте добавим несколько интерфейсов:

  1. / nodes / register Получить список новых узлов в виде URL

  2. / nodes / resolve выполняет алгоритм консенсуса, разрешает любые конфликты и гарантирует, что узел имеет правильную цепочку. Мы модифицируем функцию init Blockchain и предоставляем метод для регистрации узлов:

...from urllib.parse import urlparse...class Blockchain(object):
def __init__(self):
...
self.nodes = set()
...
def register_node(self, address):
"""
Add a new node to the list of nodes :param address: <str> Address of node. Eg. 'http://192.168.0.5:5000'
:return: None """
parsed_url = urlparse(address)
self.nodes.add(parsed_url.netloc)

Мы используем set для хранения узлов, что является простым способом избежать повторного добавления узлов.

Реализовать алгоритм консенсусаКак упоминалось ранее, конфликт означает, что разные узлы имеют разные цепочки. Чтобы решить эту проблему, оговаривается, что самая длинная и эффективная цепочка является конечной цепочкой. Другими словами, самая длинная эффективная цепочка в сети — это фактическая цепочка.

Мы используем следующий алгоритм для достижения консенсуса в сети

...import requestsclass Blockchain(object)
...
def valid_chain(self, chain):
"""
Determine if a given blockchain is valid :param chain: <list> A blockchain :return: <bool> True if valid, False if not """
last_block = chain[0]
current_index = 1
while current_index < len(chain):
block = chain[current_index]
print(f'{last_block}')
print(f'{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 if not self.valid_proof(last_block['proof'], block['proof']):
return False
last_block = block
current_index += 1
return True
def resolve_conflicts(self):
"""
 Алгоритм консенсуса для разрешения конфликтов
 Используйте самую длинную цепочку в сети. 
 : return: <bool> Истина, если цепочка заменена, иначе Ложь "" "
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:
response = requests.get(f'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

Первый метод valid_chain () используется для проверки правильности цепочки и обхода каждого блока для проверки хэша и доказательства.

Второй метод resolve_conflicts () используется для разрешения конфликтов, обхода всех соседних узлов и использования предыдущего метода для проверки достоверности цепочки. Если вы найдете действительную более длинную цепочку, замените свою собственную.

Давайте добавим два маршрута: один для регистрации узлов, а другой — для разрешения конфликтов.

@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), [email protected]('/nodes/resolve', methods=['GET'])def consensus():
replaced = blockchain.resolve_conflicts()
if replaced:
response = {
'message': 'Our chain was replaced',
'new_chain': blockchain.chain }
else:
response = {
'message': 'Our chain is authoritative',
'chain': blockchain.chain }
return jsonify(response), 200

Вы можете запускать узлы на разных машинах или открывать разные сетевые порты на одной машине, чтобы имитировать многоузловую сеть. Здесь откройте разные порты на одной машине для демонстрации. Запустите команду на разных терминалах, чтобы запустить два Узлы: http: // localhost: 5000 и http: // localhost: 5001Если вы хотите узнать, вы можете добавить QUN491308659, код: Коши, код: Коши

pipenv run python blockchain.pypipenv run python blockchain.py -p 5001

Зарегистрируйте новый узел

Затем выкопайте два блока на узле 2, чтобы убедиться, что это более длинная цепочка, а затем получите доступ к интерфейсу / узлам / разрешению на узле 1. В это время цепочка узла 1 будет заменена цепочкой узла 2 с помощью алгоритма консенсуса.

Алгоритм консенсуса для разрешения конфликтов

Хорошо, вы можете пригласить друзей протестировать ваш блокчейн

Лучшие фрилансеры в Украине › Программирование › Blockchain 145

Заказать услуги Blockchain-специалистов на Freelancehunt

Блокчейн — это революционная технология в IT-сфере. Она появилась 2009 году в качестве реестра для финансовых операций. Сегодня блокчейн-специалисты способны внедрить технологию в самые разные направления, включая банковский сектор, недвижимость и благотворительность. Их услуги разнятся по стоимости.

В Украине разработчики из этой сферы часто предлагают свои услуги на условиях фриланса, так как при налаженной системе коммуникации задачи в блокчейне могут эффективно решаться на удаленной основе. Такая схема работы удобна и для заказчика, потому что стоимость затрат на содержание фрилансера ниже, чем штатного сотрудника.

Что такое блокчейн

«Blockchain» переводится с английского языка как «цепочка блоков». Термин частично характеризует задачи технологии: блоки состоят из массивов данных, в которых указаны сведения о транзакциях и цене комиссии. Последовательно соединенные блоки формируют цепочку, в которой ни один из пользователей не может ничего изменить или удалить. Вся информация из системы находится в открытом доступе, и увидеть ее может любой желающий.

Блокчейн децентрализован: он зависит не от конкретного центрального сервера или компьютера, а от всех, кто его поддерживает. Таких удаленных пользователей еще называют майнерами. Эти специалисты используют вычислительные мощности своих компьютеров для совершения математических операций по поиску криптографического кода, за что получают финансовое вознаграждение в виде криптовалюты.

Теоретически цепочка блоков не ограничена, что делает виртуальный реестр бесконечным.

Для работы блокчейн использует шифрование данных, оставляя удаленных пользователей системы анонимными.

Применение технологии

Изначально цепочка блоков проектировалась как виртуальная база данных для хранения информации о финансовых операциях с криптовалютой Биткоин и ее цене. Сегодня специалисты успешно внедряют технологию в различные сферы, связанные с хранением данных, например, для создания CRM-систем.

Фрилансеры разрабатывают смарт-контракты для улучшения качества услуг и количества сделок в секторе недвижимости, ускоряя процесс формирования цены, покупки и продажи объектов. Заказчики из сферы юриспруденции используют блокчейн для предоставления юридических и экономических услуг.

На основе технологии создаются проекты для работы с авторскими правами. Фриланс-специалисты разрабатывают дополняемые базы данных, в которых хранятся патенты с зашифрованными идентификаторами.

Финансовый сектор также активно инвестирует в блокчейн-проекты. Банковские учреждения приглашают к сотрудничеству фрилансеров и специалистов, которые внедряют разработки, повышающие надежность системы и снижающие стоимость комиссии за перевод средств. Пользуется спросом процесс интеграции платежных систем.

Чтобы найти компетентных программистов в сфере блокчейна, выбирайте специальные сервисы, которые предлагают различные возможности для защиты сделки. Ресурс Freelancehunt предоставляет услугу Сейф: средства поступают на счет разработчика сразу после успешного выполнения им заказа. Это гарантирует защиту сделки как для фрилансера, так и для заказчика.

Изучаем блокчейн на практике

Вы читаете эту статью потому, что, как и я, с горячим интересом наблюдаете за возрастающей популярностью криптовалюты. И вам хочется понять, как работает блокчейн — технология, которая лежит в ее основе.

Но разобраться в блокчейне не так-то просто, по крайней мере, по моему опыту. Я корпел над заумными видео, продирался через туториалы и с нарастающей досадой отмечал недостаток иллюстрирующих примеров.

Я предпочитаю учиться в процессе работы. При таком раскладе мне приходится отрабатывать тему сразу на уровне кода, что помогает закрепить навык. Если вы последуете моему примеру, то к концу статьи у вас будет функционирующий блокчейн и ясное понимание, как это все работает.


Но для начала…

Напомню: блокчейн — это неизменяемая, последовательная цепочка записей, которые называются блоками. Они могут заключать в себе транзакции, файлы и, в принципе, любые другие виды данных. Главное здесь — что они связаны друг с другом посредством хэшей.

Если вы не совсем понимаете, что такое хэш, вам сюда.

На кого рассчитано это руководство? На тех, кто без проблем может читать и писать несложный код на Python и в общих чертах представляет, как работают HTTP запросы — мы будет общаться с нашим блокчейном через HTTP.

Что будет нужно для работы? Проверьте, чтобы у вас был установлен Python 3.6+ (вместе с pip). Также вам нужно будет установить Flask и прекрасную библиотеку Requests:

 pip install Flask==0.12.2 requests==2.18.4 

Ах да, еще вам понадобится HTTP клиент, например, Postman или cURL. Тут подойдет любой.

Где можно посмотреть то, что получится в итоге? Исходный код доступен здесь.

Шаг первый: Делаем блокчейн

Откройте свой любимый текстовый или графический редактор, мне вот, например, нравится PyCharm. Создайте новый файл под названием blockchain.py. Мы будем работать только в этом файле, а если запутаетесь, всегда можно подсмотреть в исходный код.

Представление блокчейна

Сначала мы создаем новый класс, конструктор которого создаст исходный пустой список (где и будет храниться наш блокчейн) и еще один — для транзакций. Вот как выглядит структура класса:

class Blockchain(object):
    def __init__(self):
        self. chain = []
        self.current_transactions = []
        
    def new_block(self):
        # Creates a new Block and adds it to the chain
        pass
    
    def new_transaction(self):
        # Adds a new transaction to the list of transactions
        pass
    
    @staticmethod
    def hash(block):
        # Hashes a Block
        pass

    @property
    def last_block(self):
        # Returns the last Block in the chain
        pass

Класс Blockchain отвечает за управление цепочкой. Здесь будут храниться транзакции, а также некоторые вспомогательные методы для добавления в цепочку новых блоков. Давайте распишем эти методы.

Как выглядит блок?

В каждом блоке содержится индекс, метка времени (в Unix), список транзакций, доказательство и хэш предыдущего блока.

Вот пример того, как может выглядет отдельный блок:

block = {
    'index': 1,
    'timestamp': 1506057125.900785,
    'transactions': [
        {
            'sender': "8527147fe1f5426f9dd545de4b27ee00",
            'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",
            'amount': 5,
        }
    ],
    'proof': 324984774000,
    'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
}

Теперь идея цепочки должна быть очевидна — каждый блок включает в себя хэш предшествующего. Это очень важно: именно так обеспечивается неизменность цепочки: если хакер повредит какой-либо блок, то абсолютно все последующие будут содержать неверные хэши.

Понятно? Если нет, остановитесь и дайте себе время усвоить эту информацию — именно в ней состоит базовый принцип блокчейна.

Добавляем транзакции в блок

Нам нужно каким-то образом добавлять в блок новые транзакции. За это отвечает метод new_transaction(), работает он достаточно просто:

class Blockchain(object):
    ...
    
    def new_transaction(self, sender, recipient, amount):
        """
        Creates a new transaction to go into the next mined Block

        :param sender: <str> Address of the Sender
        :param recipient: <str> Address of the Recipient
        :param amount: <int> Amount
        :return: <int> The index of the Block that will hold this transaction
        """

        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })

        return self. last_block['index'] + 1

Когда new_transaction() добавляет новую транзакцию в список, он возвращает индекс блока, куда она была записана, следующему, с которым будет осуществляться майнинг. Позже это пригодится следующему пользователю, добавляющему транзакцию.

Помимо создания блока genesis в конструкторе, мы также распишем методы new_block(), new_transaction() и hash():

import hashlib
import json
from time import time


class Blockchain(object):
    def __init__(self):
        self.current_transactions = []
        self.chain = []

        # Create the genesis block
        self.new_block(previous_hash=1, proof=100)

    def new_block(self, proof, previous_hash=None):
        """
        Create a new Block in the Blockchain

        :param proof: <int> The proof given by the Proof of Work algorithm
        :param previous_hash: (Optional) <str> Hash of previous Block
        :return: <dict> New Block
        """

        block = {
            'index': len(self. chain) + 1,
            'timestamp': time(),
            'transactions': self.current_transactions,
            'proof': proof,
            '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, recipient, amount):
        """
        Creates a new transaction to go into the next mined Block

        :param sender: <str> Address of the Sender
        :param recipient: <str> Address of the Recipient
        :param amount: <int> Amount
        :return: <int> The index of the Block that will hold this transaction
        """
        self.current_transactions.append({
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
        })

        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: <dict> Block
        :return: <str>
        """

        # 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()

Вышеприведенный код, вероятно, в пояснениях не нуждается — я добавил кое-где комментарии и докстринги, чтобы было понятнее. С представлением блокчейна мы практически закончили. Но сейчас вы, должно быть, задаетесь вопросом, как происходит процесс создания, встраивания и майнинга блоков.

Разбираемся с доказательством работы

Алгоритм доказательства работы служит для создания новых блоков в блокчейне (это процесс еще называется майнингом). Цель доказательства работы — вычислить нужное значение, чтобы решить уравнение. Это значение должно быть сложно рассчитать (с математической точки зрения), но легко проверить любому участнику системы. В этом заключается основная идея доказательства работы.

Чтобы стало яснее, давайте рассмотрим очень простой пример.

Допустим, хэш некоторого числа X, помноженного на другое Y, должен оканчиваться на 0. Соответственно, hash(x * y) = ac23dc…0. Для этого упрощенного примера установим x = 5. Прописываем все это на Python:

from hashlib import sha256
x = 5
y = 0  # We don't know what y should be yet...
while sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0":
    y += 1
print(f'The solution is y = {y}')

Правильный ответ здесь: y = 21; именно при таком значении получается хэш с 0 в конце:

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

В биткойне алгоритм доказательства работы называется HashCash и не особенно отличается от простенького примера, приведенного выше. Это уравнение, которые майнеры наперегонки пытаются разрешить, чтобы создать новый блок. В целом, сложность определяется тем, сколько символов нужно вычислить в заданной последовательности. За верный ответ майнеры получают вознаграждение в виде одной монеты — в ходе транзакции.

Проверить их решение для системы не составляет труда.

Пишем простое доказательство работы

Теперь давайте пропишем подобный же алгоритм для нашего блокчейна. Условия возьмем в духе вышеприведенного примера:

Найдите число p, которое, будучи хэшировано с доказательством предыдущего блока, дает хэш с четырьмя нулями в начале.

import hashlib
import json

from time import time
from uuid import uuid4


class Blockchain(object):
    ...
        
    def proof_of_work(self, last_proof):
        """
        Simple Proof of Work Algorithm:
         - Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'
         - p is the previous proof, and p' is the new proof

        :param last_proof: <int>
        :return: <int>
        """

        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        return proof

    @staticmethod
    def valid_proof(last_proof, proof):
        """
        Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?

        :param last_proof: <int> Previous Proof
        :param proof: <int> Current Proof
        :return: <bool> True if correct, False if not. 
        """

        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        return guess_hash[:4] == "0000"

Мы можем варьировать сложность этой задачи, меняя количество нулей в начале. Но четырех вполне достаточно. Вы можете сами убедиться, что один-единственный дополнительный нолик значительно замедляет процесс поиска решения.

Работа над классом почти завершена и теперь мы готовы начать взаимодействие с ним при помощи HTTP запросов.

Шаг второй: Блокчейн как API

Здесь мы будем использовать Python Flask — микрофреймворк, который облегчает процесс соотнесения конченых пунктов с функциями Python, что позволяет нам осуществлять диалог с блокчейном по Сети при помощи HTTP запросов.

Создаем три метода:

  • /transactions/new для создания новой транзакции в блоке
  • /mine для майнинга нового блока на сервере
  • /chain для возвращения полной цепочки блокчейна.

Настраиваем Flask

Наш «сервер» сгенерирует один-единственный узел сети в блокчейн-системе. Давайте напишем немного шаблонного кода:

import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4

from flask import Flask


class Blockchain(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'])
def mine():
    return "We'll mine a new Block"
  
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
    return "We'll add a new transaction"

@app.route('/chain', methods=['GET'])
def full_chain():
    response = {
        'chain': blockchain.chain,
        'length': len(blockchain.chain),
    }
    return jsonify(response), 200

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Краткие пояснения к тому, что мы добавили:

Строка 15: Инстанцирует узел. Подробнее о Flask можно почитать здесь.
Строка 18: Создает произвольное имя для узла.
Строка 21: Инстанцирует класс Blockchain.
Строки 24-26: Создает конечную точку /mine, то есть запрос GET.
Строки 28-30: Создает конечную точку /transactions/new, то есть запрос POST, так как именно туда мы и будем отсылать данные.
Строки 32-38: Создает конечную точку /chain, который возвращает блокчейн целиком.
Строки 40-41: Запускает сервер на порту 5000.

Конечный пункт для транзакций

Вот как будет выглядеть запрос на транзакцию. Именно это пользователь отсылает на сервер:

{
 "sender": "my address",
 "recipient": "someone else's address",
 "amount": 5
}

Метод класса для добавления транзакции в блок у нас уже есть, поэтому дальше все легко. Давайте напишем функцию для добавления транзакции:

import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4

from flask import Flask, jsonify, request

. ..

@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', 'recipient', 'amount']
    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['recipient'], values['amount'])

    response = {'message': f'Transaction will be added to Block {index}'}
    return jsonify(response), 201

Конечный пункт для майнинга

Именно в этой конечной точке творится вся магия, но ничего особо сложного в нем нет. Она должна делать три вещи:

  1. Рассчитывать доказательство работы
  2. Выдавать майнеру (то есть нам) вознаграждение, добавляя транзакцию, с ходе которой мы получаем одну монету
  3. Встраивать новый блок в цепочку
import hashlib
import json

from time import time
from uuid import uuid4

from flask import Flask, jsonify, request

. ..

@app.route('/mine', methods=['GET'])
def mine():
    # 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)

    # We must receive a reward for finding the proof.
    # The sender is "0" to signify that this node has mined a new 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'],
    }
    return jsonify(response), 200

Обратите внимание, что в качестве получателя созданного блока указан адрес узла. Большая часть того, что мы тут делаем, сводится к взаимодействию с методами нашего класса Blockchain. По завершению этого шага основная работа закончена, можно начинать диалог.

Шаг третий: Диалог с блокчйном

Для взаимодействия с API в рамках системы можно использовать старый-добрый cURL или Postman.

Запускаем сервер:

$ python blockchain.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Давайте попробуем создать блок, отправив запрос GET по адресу localhost:5000/mine:

Теперь создаем новую транзакцию, отправив запрос POST, содержащий ее структуру, по адресу localhost:5000/transactions/new:

Если вы работаете не с Postman, вот как сформулировать аналогичный запрос в cURL:

$ curl -X POST -H "Content-Type: application/json" -d '{
 "sender": "d4ee26eee15148ee92c6cd394edd974e",
 "recipient": "someone-other-address",
 "amount": 5
}' "http://localhost:5000/transactions/new"

Я перезапустил сервер и создал еще два блока, чтобы в итоге получилось три. Давайте изучим получившуюся цепочку через запрос 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
}

Шаг четвертый: Консенсус

Все это очень здорово. У нас есть простой блокчейн, который позволяет осуществлять транзакции и создавать новые блоки. Но блокчейн имеет смысл только в том случае, если он децентрализован. А если сделать его децентрализованным, как мы вообще можем гарантировать, что везде будет отображаться одна и та же цепочка? Это называется проблемой консенсуса. Если мы хотим, чтобы в системе было больше одного узла, придется ввести алгоритм консенсуса.

Распознаем новые узлы

Прежде чем внедрять алгоритм консенсуса, нам нужно что-то предпринять, чтобы каждый узел в системе знал о существовании соседних. У каждого узла в системе должен быть реестр всех остальных узлов. А значит понадобятся дополнительные конечные точки:

  1. /nodes/register, который будет принимать список новых узлов в URL формате
  2. /nodes/resolve для внедрения алгоритма консенсуса, который будет разрешать возникающие конфликты и отслеживать, чтобы в узле содержалась правильная цепочка.

Нам нужно подкорректировать конструктор блокчейна и обеспечить метод для регистрации узлов:

...
from urllib.parse import urlparse
...


class Blockchain(object):
    def __init__(self):
        ...
        self.nodes = set()
        ...

    def register_node(self, address):
        """
        Add a new node to the list of nodes

        :param address: <str> Address of node.  Eg. 'http://192.168.0.5:5000'
        :return: None
        """

        parsed_url = urlparse(address)
        self.nodes.add(parsed_url.netloc)

Заметьте: мы использовали set() для хранения списка узлов. Это нехитрый способ гарантировать, что при добавлении новых узлов будет соблюдаться индемпотентность — то есть сколько бы раз мы ни добавляли какой-то конкретный узел, он будет засчитан только единожды.

Внедряем алгоритм консенсуса

Как я уже упоминал, конфликт происходит тогда, когда цепочка одного узла отличается от цепочки другого. Чтобы его устранить, мы введем такое правило: прерогатива всегда у той цепочки, которая длиннее. Иными словами, самая длинная цепочка в системе рассматривается как фактическая. Используя такой алгоритм, мы достигаем консенсуса среди всех узлов системы:

...
import requests


class Blockchain(object)
    ...
    
    def valid_chain(self, chain):
        """
        Determine if a given blockchain is valid

        :param chain: <list> A blockchain
        :return: <bool> True if valid, False if not
        """

        last_block = chain[0]
        current_index = 1

        while current_index < len(chain):
            block = chain[current_index]
            print(f'{last_block}')
            print(f'{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
            if not self.valid_proof(last_block['proof'], block['proof']):
                return False

            last_block = block
            current_index += 1

        return True

    def resolve_conflicts(self):
        """
        This is our Consensus Algorithm, it resolves conflicts
        by replacing our chain with the longest one in the network.

        :return: <bool> True if our chain was replaced, False if not
        """

        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:
            response = requests.get(f'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

Первый метод valid_chain() отвечает за проверку цепочек на валидность, проходя каждый блок и верифицируя и хэш, и доказательство.

resolve_conflicts() — метод, который прорабатывает все соседние узлы: скачивает их цепочки и проверяет их описанным выше способом. Если при этом найдена валидная цепочка длиннее, чем наша, производится замена.

Давайте введем в наш API две конечные точки, один для добавления соседних узлов, другой для разрешения конфликтов:

@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('/nodes/resolve', methods=['GET'])
def consensus():
    replaced = blockchain.resolve_conflicts()

    if replaced:
        response = {
            'message': 'Our chain was replaced',
            'new_chain': blockchain.chain
        }
    else:
        response = {
            'message': 'Our chain is authoritative',
            'chain': blockchain.chain
        }

    return jsonify(response), 200

На данном этапе, если хотите, можете привлечь другие машины и насоздавать разных узлов для вашей системы. Или добиться того же используя разные порты на одной машине. Я создал новый узел на другом порте той же машины, и позволил исходному узлу его распознать. Таким образом, получилось два узла: localhost:5000 и localhost:5001.

В узел номер два я добавил побольше блоков, чтобы цепочка получилась однозначно длиннее. После чего вызвал GET /nodes/resolve в первом узле — и алгоритм консенсуса заменил его цепочку на цепочку второго.

Ну, вот и все. Теперь собирайте друзей и тестируйте вам блокчейн совместными усилиями.

Надеюсь, этот материал вдохновит вас на новые идеи. Лично я с большим энтузиазмом наблюдаю за развитием криптовалюты: я уверен, что блокчейн перевернет наши представления об экономике, управлении государством и хранении информации.

В будущем я планирую выпустить вторую часть статьи, где мы добавим в блокчейн механизм валидации транзакций и поговорим о том, как все это можно использовать в продуктах.

Автор: nanton

Источник

Доступ к Trello API в Python скриптах с помощью Connect Bridge

Хотите получить доступ к Trello API с помощью скриптов Python быстро и без необходимости изучать Trello API? Читайте это руководство и узнайте, как легко получить доступ к Connect Bridge.

Трелло API Питон — Введение

Никто не любит изучать длинную и сложную документацию для API каждый раз, когда возникает необходимость доступа к ней. В этой статье описывается, как получить доступ к Trello API через языковые скрипты Python (пробовали в версии 3.3), не имея никакого опыта работы с API Trello. Для этого учебника вам понадобится интеграционная платформа Connect Bridge, позволяющая осуществлять обмен данными через Trello API, используя в основном простые SQL-запросы. Передача данных осуществляется через уровень ODBC. Я добился этого на Python, используя модуль pyodbc ver. 3.0.7 x64

Что такое «1ТП16Т»?

Connect Bridge является интеграционной платформой, которая позволяет подключать любое программное обеспечение через ODBC, JDBC драйверы и веб-службы с более чем 31 коннектором (включая Trello). Разработчики могут получить доступ к этим разъемам на любом языке программирования без необходимости иметь опыт работы с целевой системой, в основном в течение нескольких минут. Дело в том, что вы можете использовать инструмент для доступа не только к Trello API через Python, вы также можете захватывать/выводить данные из/в MS Exchange, MS Dynamics CRM, MS SharePoint и многих других систем.
Предпосылки

Теперь давайте рассмотрим процесс разработки простого Python скрипта, который обращается к Trello API с помощью Connect Bridge через ODBC драйвер в несколько простых шагов:

  1. Установите Connect Bridge (вы можете получить бесплатная пробная версия здесь)
  2. Установить Питон для Windows Вер. 3.3+
  3. Установить модуль пиодбк 3.0.7+ (я использовал pyodbc-3.0.7.win-amd64-py3.3.exe)
  4. Для того, чтобы облегчить написание и выполнение и отладку моих сценариев, я использовал Затмение Луна с пыдев плагин
  5. Установка учетной записи для Trello в Connect Bridge (см. параграф ниже)
Connect Bridge настройка

После установки Connect Bridge можно увидеть три приложения:

  • Контроллер CB Service — служит для запуска и остановки Connect Bridge
  • Инструмент администрирования CB — здесь вы можете настроить свои аккаунты для подключения к целевой системе
  • Анализатор запросов CB Query Analyzer — этот инструмент служит для визуализации схемы базы данных целевой системы, и вы можете легко попробовать здесь свои запросы.

Так что давай создадим твой аккаунт для Трелло. Откройте инструмент администрирования и в разделе Аккаунты создайте новый аккаунт, в поле Коннекторы выберите CBTrelloConnector и нажмите Мастер Аутентификации.

Там ты просто заполняешь свое имя пользователя и пароль и разрешаешь доступ к твоему Trello.

Тогда просто сохрани его, и у нас будет наш счет.
Теперь мы можем использовать инструмент CB Query Analyzer для визуализации схемы. Нам необходимо добавить новое соединение, как описано на рисунке ниже.

Для подключения от питоновского скрипта нам понадобится строка подключения к этому аккаунту.

Чтобы узнать нашу строку соединения, достаточно щелкнуть правой кнопкой мыши по соединению с Trello, а затем нажать кнопку «Редактировать соединение», в редакторе соединений выбрать закладку «Дополнительно» и скопировать значение из текстового поля «Строка соединения». (См. рисунки ниже)

Руки на сценарии!

Ядро и в то же время единственный файл в моем решении — CBQuery. py. Пожалуйста, найдите исходный код ниже.

 #!/usr/local/bin/python2.7
# кодировка: utf-8
'''
CBQuery2 -- запрос данных из, запись данных в SharePoint, Dynamics CRM, Exchange
CBQuery2 - это скрипт, позволяющий выполнять SQL-запросы через ODBC-драйвер Connect Bridge.

@author:     Michal Hainc
@copyright: 2014
@лицензия:    Apache 2.0

@contact: [email protected]
@deffield updated: 18.8.2014
'''

импортировать sys
импортировать os
импортировать pyodbc

from argparse import ArgumentParser
from argparse import RawDescriptionHelpFormatter

__all__ = []
__version__ = 0.1
__date__ = '2014-08-15'
__updated__ = '2014-08-15'

DEBUG = 1
ТЕСТРУН = 0
ПРОФИЛЬ = 0

class CLIError(Exception):
    '''Общее исключение для создания и регистрации различных фатальных ошибок.'''
    def __init__(self, msg):
        super(CLIError).__init__(type(self))
        self.msg = "E: %s" % msg
    def __str__(self):
        return self.msg
    def __unicode__(self):
        return self.msg

def main(argv=None): # IGNORE:C0111
    '''Параметры командной строки. '''

    если argv = None:
        argv = sys.argv
    else:
        sys.argv.extend(argv)

    имя_программы = os.path.basename(sys.argv[0])
    program_version = "v%s" % __version__
    program_build_date = str(__updated__)
    program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date)
    program_shortdesc = __import__('__main__').__doc__.split("n")[1]
    program_license = '''%s

  Создано Михалом Хайнцем на %s.

  Лицензируется по лицензии Apache License 2.0
  http://www.apache.org/licenses/LICENSE-2.0

  Распространяется на условиях "КАК ЕСТЬ" без гарантий
  или условий любого рода, явных или подразумеваемых.

ИСПОЛЬЗОВАНИЕ
''' % (program_shortdesc, str(__date__))

    попытка:
        # Настройка анализатора аргументов
        parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter)
        parser.add_argument('connstr')
        parser.add_argument('query')
        
        Аргументы процесса #
        args = parser. parse_args()

        query = args.query
        connstr = args.connstr

        conn = pyodbc.connect(connstr)
        cursor = conn.cursor()
        cursor.execute(query)
        while 1:
            row = None
            попытка:
                row = cursor.fetchone()
            except:
                print(sys.exc_info()[1])
                break
            if not row:
                break
            print(row)
                        
            
    except KeyboardInterrupt:
        ### обработать прерывание клавиатуры ###
        возврат 0
    кроме:
        print(sys.exc_info()[1])
        #indent = len(имя_программы) * " "
        #sys.stderr.write(имя_программы + ": " + repr(e) + "n")
        #sys.stderr.write(indent + " для справки используйте --help")
        возврат 2

if __name__ == "__main__":
         
    if TESTRUN:
        import doctest
        doctest.testmod()
    if PROFILE:
        import cProfile
        import pstats
        profile_filename = 'CBQuery2_profile. txt'
        cProfile.run('main()', profile_filename)
        statsfile = open("profile_stats.txt", "wb")
        p = pstats.Stats(profile_filename, stream=statsfile)
        stats = p.strip_dirs().sort_stats('cumulative')
        stats.print_stats()
        statsfile.close()
        sys.exit(0)
    sys.exit(main())
  • connectstr (строка соединения) содержит строку соединения ODBC, которую мы передаем в модуль pyodbc для создания соединения ODBC, хранящегося в переменной conn
  • в качестве следующего шага мы открываем курсор базы данных, используя соединение, хранящееся в подключении.
  • затем мы выполняем SQL-запрос, переданный через параметр командной строки с именем запроса.
  • как и в прошлый раз, мы используем цикл для чтения результатов от курсора ряд за рядом, когда курсор.fetchone возвращает None, мы прерываем цикл
  • если во время извлечения ODBC происходит исключение, мы также прерываем цикл и выводим на печать проблему.
  • если метод fetchone будет продолжать и вернет строку данных, мы распечатаем строку необработанных данных на выход для демонстрационных целей (. .. может быть любой вид вывода… мы можем отформатировать как xml или json, csv… или любой вид формата обмена данными) или просто используем объект строки необработанных данных, который будет использоваться в дальнейшей логике кода для выполнения пользовательских задач
Запуск сценария CBQuery.py

Я протестировал свой скрипт с помощью Windows PowerShell, но вы можете запустить любую другую оболочку, которую захотите использовать…

Мы знаем, что наш CBQuery.py принимает два аргумента позиционной командной строки — «конструкт» и «запрос».

Мы видим, что схема содержит «таблицу» под названием «список», поэтому мы можем построить наш запрос как SELECT * FROM list LIMIT 10, чтобы выбрать первые 10 записей из списка Trello, и запустив запрос, как мы видим на нашем рисунке ниже, что запрос вернул 10 строк в выводе.

Теперь мы можем попробовать создать новую карту Trello в первый список «Пустых». Мы будем использовать для этого запрос:

INSERT INTO Card (имя, idlist) VALUES («Новая карта», ‘565c0d32c75039b07e86b0b3’)

В Трелло можно увидеть, что новая карта была успешно создана.

Ограничения

До сих пор я мог использовать инструмент Connect Bridge только из операционных систем Windows, благодаря тому, что Connecting Software не предоставил никакой клиентской библиотеки ODBC Linux (если это вообще возможно). Поэтому не забудьте поиграть со своими Python скриптами на машине под управлением Windows.

Заключение

Мы доказали, что доступ к Trello API на Python может быть действительно простым с помощью инструмента интеграции Connect Bridge. И Trello — это только одна система из множества других, которую Connect Bridge позволяет интегрировать. Представьте, что вы можете безопасно получить доступ к API основных программных продуктов и услуг, таких как Dynamics CRM, NAV, AX, Salesforce, Exchange и многих других. Если вы заинтересованы в том, чтобы перестать тратить время на изучение сотен страниц документации по API каждый раз, когда вы сталкиваетесь с новой целевой системой, запросить бесплатную пробную версию, цены и любые дополнительные вопросы.

Спасибо,
Ondrej