主页 > imtoken苹果闪退 > 简单版python区块链交易实现实例

简单版python区块链交易实现实例

imtoken苹果闪退 2023-01-29 06:57:40

内容介绍

基于本文内容,采用python实现,但根据个人理解做了一些修改,并引用了大量原文。 文末有“本节完整源码实现地址”。

介绍

交易是比特币的核心,区块链的唯一目的就是安全可靠地存储交易。 在区块链中,交易一旦创建,任何人都无法修改或删除。 今天,我们将开始实现事务。 但是,由于交易是一个很大的话题,我将其分为两部分: 在今天的部分中,我们将实现交易的基本框架。 在第二部分,我们将继续讨论它的一些细节。

由于比特币采用的是UTXO模型,而非账户模型,因此“余额”的概念并不直接存在,需要遍历整个交易历史来获取余额。

比特币交易

点此查看下图中的交易信息/uploads/tupian/20220822/xlwezvx5ghu。

python区块链简易版交易实现示例

交易由一些输入和输出组成:

class Transaction(object):
    def __init__(self, vins, vouts):
        self.txid = ''
        self.vins = vins
        self.vouts = vouts

对于每一笔新的交易,它的输入都会参考上一笔交易的输出(这里有个例外,coinbase交易),参考就是花费的意思。 所谓引用之前的输出,就是将之前的输出包含在另一笔交易的输入中,也就是花费之前的交易输出。 交易的输出是硬币实际存储的地方。 下图说明了事务之间的相互关系:

python区块链简易版交易实现示例

注意:

在整篇文章中,我们将使用“金钱”、“硬币”、“花费”、“发送”、“帐户”等词。 但是在比特币中,没有这样的概念。 一个交易只是一个脚本(script)来锁定(lock)一些值(value),而这些值只能被锁定它们的人解锁(unlock)。

每笔比特币交易都会产生一个输出,并记录在区块链上。 将比特币发送给某人实际上意味着创建一个新的 UTXO 并将其注册到该人的地址,该地址可供他使用。

交易输出

让我们从输出开始:

class TXOutput(object):
    def __init__(self, value, script_pub_key):
        self.value = value
        self.script_pub_key = script_pub_key

输出主要包括两部分:

一定数量的比特币(价值)

必须为此金额解锁的锁定脚本 (script_pub_key)。

事实上,它是存储“硬币”的输出(注意,就是上面的 Value 字段)。 这里的存储指的是用一个数学谜题来锁定输出,这个谜题保存在script_pub_key中。 在内部,比特币使用一种称为 Script 的脚本语言来定义锁定和解锁输出的逻辑。 虽然该语言相当原始(故意避免潜在的黑客攻击和误用)且不复杂,但我们不会在这里讨论它的细节。 您可以在此处找到详细说明。

在比特币中,值字段存储聪的数量,而不是 BTC 的数量。 一个聪等于一个 BTC 的亿分之一(0.00000001 BTC),这也是比特币中最小的货币单位(如 1 美分硬币)。

由于地址尚未实现,我们将暂时避免涉及逻辑的完整脚本。 script_pub_key 将存储任意字符串(用户定义的钱包地址)。

顺便说一句,有了这样的脚本语言,也意味着比特币实际上可以作为一个智能合约平台。

关于产出的一个非常重要的一点:它们是不可分割的。 也就是说,你不能只引用它的一部分。 要么不用,要用就得一下子用完。 当在新交易中引用输出时,输出必须完全花费。 如果它的值大于要求,则生成一个更改返回给发送者。 这与现实世界的场景非常相似,当你想支付时,如果某件东西价值 1 美元,你给了 5 美元的钞票,那么你会得到 4 美元的找零。

发送硬币

现在,我们想发送一些硬币给其他人。 为此,我们需要创建一个新交易,将其放入一个区块中,然后挖掘该区块。 以前我们只实现了 coinbase 交易(这是一种特殊的交易),现在我们需要一个通用的普通交易:

def new_transaction(self, from_addr, to_addr, amount):
        inputs = []
        outputs = []
        acc, valid_outpus = self._find_spendable_outputs(from_addr, amount)
        if acc < amount:
            raise NotEnoughAmountError(u'not enough coin')
        for txid, outs in valid_outpus.items():
            for out in outs:
                out_index = out[0]
                input = TXInput(txid, out_index, from_addr)
                inputs.append(input)
        output = TXOutput(amount, to_addr)
        outputs.append(output)
        if acc > amount:
            # a change
            outputs.append(TXOutput(acc - amount, from_addr))
        tx = Transaction(inputs, outputs)
        tx.set_id()
        return tx

在创建新的输出之前,我们必须首先找到所有未花费的输出并确保它们具有足够的价值(value)比特币交易速度是每秒多少笔,这就是 _find_spendable_outputs 方法所做的事情。 然后,对于找到的每个输出,都会创建一个引用该输出的输入。 接下来,我们创建两个输出:

一个被收件人地址锁定。 这是将硬币实际转移到其他地址。

一个被发件人地址锁定。 这是一个变化。 只有当未花费的输出超过新交易所需时才会产生。 记住:输出是不可分割的。

此方法遍历所有未花费的交易并累积其价值。 当累计值大于或等于我们要发送的值时,它停止并返回累计值,以及按交易 ID 分组的输出索引。 我们只需要提取足够的钱来支付。

现在我们修改add_block方法:

    def add_block(self, transactions):
        """
        add a block to block_chain
        """
        last_block = self.get_last_block()
        prev_hash = last_block.get_header_hash()
        height = last_block.block_header.height + 1
        block_header = BlockHeader('', height, prev_hash)
        block = Block(block_header, transactions)
        block.mine()
        block.set_header_hash()
        self.db.create(block.block_header.hash, block.serialize())
        last_hash = block.block_header.hash
        self.set_last_hash(last_hash)

最后,让我们实现发送方法:

def send(bc, from_addr, to_addr, amount):
    bc = BlockChain()
    tx = bc.new_transaction(from_addr, to_addr, amount)
    bc.add_block([tx])
    print('send %d from %s to %s' %(amount, from_addr, to_addr))

发送硬币意味着创建新的交易并通过挖掘新区块将其打包到区块链中。 然而,比特币不会一次性完成所有这些事情(尽管我们当前的实现是这样做的)。 相反,它将所有新交易放入一个内存池中比特币交易速度是每秒多少笔,然后当矿工准备好挖掘一个新区块时,它会从内存池中提取所有交易,创建一个候选区块。 只有当包含这些交易的区块被挖掘并添加到区块链时,其中的交易才开始被确认。

让我们检查发送硬币是否有效:

首先,我们需要执行main.py来完成创世块的构建

$ python3 main.py
Mining a new block
Found nonce == 17ash_hex == 01ded3ff2872093f2eefcd7b8b5b264e96996f31f933e6636db034b4151b61aa
Block(_block_header=BlockHeader(timestamp='1551086196.4749706', hash_merkle_root='', prev_block_hash='', hash='ce93f6e1a2f7dec3a538e8b6397e4b8eba59bace2e7ac08f82875447d2660173', nonce=None, height=0))
Block(_block_header=BlockHeader(timestamp='1551086196.6248493', hash_merkle_root='', prev_block_hash='', hash='d5ecad2ed10a978e2f280e62d6a25ce4def6cdfc66ac9dcd124c24c5a4b9ac07', nonce=17, height=1))

$ python3 cli.py send --from zhangsanaddr --to lisiaddr --amount 10
Found nonce == 0ash_hex == 08c67066d0c7fc8d2ef80076e91626ff05999046ae0248e1971b99a30541518b
send 10 from zhangsanaddr to lisiaddr

余额检查

def get_balance(bc, addr):
    balance = 0
    utxos = bc.find_UTXO(addr)
    for utxo in utxos:
        balance += utxo.value
    print('%s balance is %d' %(addr, balance))

find_UTXO方法查找所有的UTXO,如下:

$ python3 cli.py balance zhangsanaddr 
zhangsanaddr balance is 980

先用_find_unspent_transactions找出所有未花费的交易,判断当前地址是否可以解锁,然后找出所有UTXO。

测试效果:

$ python3 cli.py balance zhangsanaddr 
zhangsanaddr balance is 980

总结

这并不容易,但现在终于达成协议! 但是,我们仍然缺少一些关键功能,例如比特币:

地址。 我们还没有基于私钥的真实地址。

奖赏(奖赏)。 现在挖矿肯定是赚不到钱的!

UTXO 集合。 获取余额需要扫描整个区块链,当块很多时,这可能需要很长时间。 另外,如果我们要验证后续交易,将需要很长时间。 UTXO 集合就是为了解决这些问题,加速交易相关的操作。

内存池(mempool)。 在交易被打包成区块之前,这些交易存储在内存池中。 在我们目前的实现中,一个区块只包含一个交易,效率很低。

参考:

[1]

[2] 本节完整实现源码

以上就是python区块链简单版交易实现实例的详细内容。 更多关于python区块链交易的内容,请关注站长源码网其他相关文章!