python创建入门级比特币!

I have yet to meet a person who understands blockchain but don't believe in it.                            ——by CZ

本文将在前文基础上使用python实现一条简单的链。
源码地址PYchain/blockchain


创世区块

 第一个区块——创始区块的信息直接定义,以后的区块信息要在挖矿时计算得出。 区块内容格式如下图函数定义:

  • index代表区块高度,代表是第几个区块,从0开始
  • merkle_tree是transaction里的交易哈希之后最后得到的默克尔根
  • previous_hash 是前一个区块的哈希值
  • proof是该区块的工作量证明
  • timestamp代表区块创建时间
  • transaction包括当前区块包含的交易
def genesis_block(self):
        block = {
            'index': 0,
            'merkle_tree': '000000',
            'previous_hash': '000000',
            'proof': 138,
            'timestamp': 1559273178.3706222,
            'transactions': [],
        }
        self.chain.append(block)

创建链

链用一个列表来表示,将上边的创世区块加进来,至此,区块 + 链 = 区块链!!!

self.chain = []
self.genesis_block()

挖矿奖励交易

 当你通过工作量证明组装区块时,区块中的第一笔交易必须是挖矿奖励。 挖矿奖励amount为50,接受者是你的公钥,发送者是系统(Satoshi只是一个名称,可以是任意字符) 为了以后计算merkle root,还需计算这条交易的哈希值,用txhash表示。

#  初始化coin_base奖励
self.mine_transaction = {'amount': 50, 'recipient': str(self.public_key), 'sender': 'Satoshi'}
self.coin_base = [[self.mine_transaction, {'txhash': sha_256(self.mine_transaction)}]]

挖矿

挖矿(proof of work)就是找到一个符合条件的新区块。 对区块的各个内容填充:

  • 计算当前区块的高度
  • 计算默克尔根
  • 将proof初始化为0
  • 得到当前时间
  • 将挖矿交易、本地的交易和接收到的交易加入transactions
    def proof_of_work(self):
        # coin_base交易结构[[{tx},{txhash}]]
        proof = 0
        t = time()
        mk = [self.coin_base[0][1]['txhash']]
        for tx in self.current_transactions:
            mk.append(tx[2]['txhash'])
        block = {
            'index': len(self.chain),
            'merkle_tree': merkle_tree(mk),
            'previous_hash': sha_256(self.chain[-1]),
            'proof': proof,
            'timestamp': t,
            'transactions': self.coin_base + self.current_transactions + self.receive_transactions,
        }
        while self.valid_proof(block) is False:
            proof += 1
            block['proof'] = proof
        self.current_transactions = []
        self.receive_transactions = []
        self.chain.append(block)
        self.utxo_pool(block)
        return block

 下面开始做工作量证明,计算组装区块的哈希值,如果哈希值的前2位(difficult设置为2)不等于'00',’proof加1,继续计算组装区块的哈希,直到找到一个满足条件的proof值,这个proof满足区块哈希之后前两位为0。完成工作量证明之后,将当前交易池清空,因为已经产生了一个区块将这些交易信息包含了,将区块加到链上。(比特币当前的难度大约需要前19位为0)

    def valid_proof(block, difficulity=DIFFICULTY):
        guess_hash = sha_256(block)
        return guess_hash[:difficulity] == "0"*difficulity

计算公钥和当前余额

计算你的公钥当前拥有多少钱,公钥的收入支出有:
 * 挖矿
 * 别人支付给你
 * 你支付给别人
 当有新区块产生时,如果区块是当前节点挖出的话,那么区块transaction里的第一条交易的接受者就是当前节点的公钥,挖矿奖励,余额+50,遍历剩余交易,如果交易的发送者(sender)是节点公钥的话,余额减去相应的amount,余下同理。

    def utxo_pool(self, block):
        # 交易提交之前检验余额是否足够
        if block['transactions'][0][0]['recipient'] == str(self.public_key):
            self.amount = self.amount + 50
        for tx in block['transactions'][1:]:
            if tx[0]['sender'] == str(self.public_key):
                self.amount = self.amount - tx[0]['amount']

            if tx[0]['recipient'] == str(self.public_key):
                self.amount = self.amount + tx[0]['amount']

提交交易

    def sub_transaction(self, recipient, amount):
        # 交易结构[[t1,t2,t3]]
        if amount > self.amount:
            return False
        else:
            t1 = {# 字典顺序不能修改,否则网络传输过程中改变最终hash值
                    'amount': amount,
                    'recipient': recipient,
                    'sender': str(self.public_key),
                    }
            signature = sign(t1, self.private_key)
            t2 = {'signature': signature}
            t3 = {'txhash': sha_256(t1)}
            tx = [t1, t2, t3]
            self.current_transactions.append(tx)
            return True

 支付给其他人,就是提交交易,这条消息被写入区块,交易完成。 首先查询余额,余额足够才能正确提交交易。 然后填写交易信息t1,填写支付金额,填写接受者,发送者为当前节点公钥;然后对t1签名,然后计算交易哈希,最后将完整的一条交易信息加入本地交易池,注意加入交易池不代表交易完成,只有当交易被写入区块才算交易完成


节点交互

  上边的内容你已经拥有一条链了,可以通过工作量证明进行挖矿,可以提交交易(使用币),可以查询自己的余额,慢慢该链的长度变长。 接下来,当有一个新的节点加入时,两个节点有两条链,虽然创世区块是一样的,但之后的区块完全不一样,比特币的规则是谁的链长谁是真的链。

  首先得到邻居节点(neighbor)的链的长度和链. 然后判断谁的链长度最长,如果邻居的链长度是较长的,检验邻居链的合法性,合法后将自身链转变为邻居链 当自身链被替换后,需要重置账户余额,遍历新链来计算,并将交易池清空 。

    def resolve_conflicts(self):
        new_chain = None
        max_length = len(self.chain)
        if len(self.neighbor) == 0:
            pass
        else:
            for node in self.neighbor:
                response = requests.get(f'http://{node}/chain')
                if response.status_code == 200:
                    length = response.json()['length']
                    chain = response.json()['chain']
                    if length > max_length and self.valid_chain(chain):
                        max_length = length
                        new_chain = chain
        if new_chain:
            self.chain = new_chain
            self.amount = 0
            for block in self.chain[1:]:
                self.utxo_pool(block)
            self.current_transactions = []
            self.receive_transactions = []
            return True
        return False

  判断链的合法性有以下几个方面,从链的创始区块开始,每个节点都要独立验证链上所有信息的合法性。

  • 每个区块的的previous_hash是否是上一个区块的哈希
  • 每个区块的工作量证明是否正确
  • 每个区块包含的交易是否正确
    def valid_chain(self, chain):
        last_block = chain[0]
        current_index = 1
        while current_index < len(chain):
            block = chain[current_index]
            last_block_hash = sha_256(last_block)
            if block['previous_hash'] != last_block_hash:
                self.msg.append('hash wrong')
                return False
            if not self.valid_proof(block):
                self.msg.append('proof wrong')
                return False
            if not self.valid_block_transaction(block):
                self.msg.append('t wrong')
                return False
            last_block = block
            current_index += 1
        return True

关于如何验证交易正确与否,就是验签。

    def valid_block_transaction(block):
        transactions = block['transactions'][1:]
        if len(transactions) == 0:
            return True
        else:
            for tx in transactions:
                if not (verify_sign(tx[0], eval(tx[0]['sender']), tx[1]['signature'])):
                    return False
            return True

参考资料

Mastering Bitcoin Andreas Antonopoulos
Learn Blockchains by Building One
https://hackernoon.com/learn-blockchains-by-building-one-117428612f46
A Practical Introduction to Blockchain with Python http://adilmoujahid.com/posts/2018/03/intro-blockchain-bitcoin-python/

你可能感兴趣的:(python创建入门级比特币!)