首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Python探索BlockChain的基本知识

使用Python探索BlockChain的基本知识
EN

Code Review用户
提问于 2020-03-30 00:34:09
回答 1查看 88关注 0票数 3

当我是流式传输时,我有一位出色的访问者建议我们用Python编写一个区块链。

我们就这么做了。

请注意,这里没有验证或投票模拟。

以下是package/blockchain.py中的结果:

代码语言:javascript
复制
from __future__ import annotations
# https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports
from oscrypto.symmetric import aes_cbc_pkcs7_encrypt as encrypt # type: ignore


class Node:
    __slots__ = 'prior_node', 'data', 'hash'
    prior_node: Node
    data: bytes
    hash: bytes

    def __init__(self, prior_node, key, data):
        self.prior_node = prior_node
        self.data = data
        if prior_node is None:
            init_vector = bytes(16)
        else:
            init_vector = _ensure_byte_length(prior_node.hash, 16)
        key = _ensure_byte_length(key, 32)
        self.hash = encrypt(key, data, init_vector)[1]

    def __repr__(self):
        return f'Node<{self.data}\n{self.hash}\n{self.prior_node}>'

def _ensure_byte_length(bytes_, length):
    return bytes(bytes_.ljust(length, b'\x00')[:length])

class Chain:
    __slots__ = 'nodes'
    def __init__(self, key: bytes, data: bytes):
        self.nodes = Node(None, key, data)

    def __repr__(self):
        return f'Chain:\n{self.nodes}'

    def new_node(self, key, data):
        self.nodes = node = Node(self.nodes, key, data)
        return node

    def __len__(self):
        length = 0
        nodes = self.nodes
        while nodes:
            length += 1
            nodes = nodes.prior_node
        return length

def main():
    chain = Chain(b'the key', b'here is a bit of data')
    chain.new_node(b'P@$w0rd', b'and here is a bit more data')
    chain.new_node(b'hunter2', b'and finally here is some more')
    print(chain)


if __name__ == '__main__':
    main()

下面是一个小小的测试,tests/test_blockchain.py

代码语言:javascript
复制
from package.blockchain import Chain


def test_blockchain():
    chain = Chain(b'the key', b'here is a bit of data')
    chain.new_node(b'P@$w0rd', b'and here is a bit more data')
    chain.new_node(b'hunter2', b'and finally here is some more')
    assert len(chain) == 3

请注意,我们确实需要oscrypto和openssl来运行它。

在Python3.7下使用pytest运行覆盖率。带着黑色和伤感跑。

EN

回答 1

Code Review用户

发布于 2020-03-30 02:31:13

  1. 默认情况下,mypy测试不多。这是因为在很大程度上,它的理念是同时允许动态和静态类型的代码。这是如此的迁移到类型是更容易和不那么令人生畏。当您开始移植遗留应用程序时,可能会有数千个错误,这可能会吓跑一些普通人。请使用--strict标志输入Python,而不是混合Python。当您使用该标志并键入所有函数和方法时,您会注意到Node.prior_node存在问题。目前它被指定为Node类型,但是我们知道这是一个谎言,因为我们有if prior_node is None
  2. 我个人使用--ignore-missing-imports,而不是忽略每个导入,因为它们会随着时间的推移迅速积累起来。
  3. 你的__repr__非标准。由瑞尔()内置函数调用,以计算对象的“正式”字符串表示形式。如果可能的话,这应该看起来像一个有效的Python表达式,可以用来重新创建具有相同值的对象(给定适当的环境)。如果这是不可能的,则应该返回表单<...some useful description...>的字符串。您可能想要使用__str__
  4. 我发现init_vector被分配了两种不同的东西,这有点令人困惑。如果将空字节传递给_ensure_byte_length,则会更有意义。如果prior_hash = b'‘如果prior_node不是其他的prior_node.hash init_vector = _ensure_byte_length(prior_hash,16),则可以将三元改为getattr。prior_hash = getattr(prior_hash,‘散列’,b'')
  5. 我会将Node改为数据集。既然你不能写代码,为什么要写呢?这需要将哈希生成移到类方法中。
  6. 我将在节点上定义一个__iter__方法,这样我们就可以轻松地从任何节点遍历链。这使得ChainD29方法非常简单和干净。
  7. 我会把_ensure_byte_length改名为_pad。该函数有两个任务,pad是众所周知的,并允许我们有一个更短的功能名称。
  8. _ensure_byte_length不需要额外的bytes调用。
  9. 方法Chain.add_node是非Pythonic的.在Python中,从带有突变的函数中不返回任何内容是标准的。
代码语言:javascript
复制
from __future__ import annotations

import dataclasses
from typing import Optional, Iterator

from oscrypto.symmetric import aes_cbc_pkcs7_encrypt as encrypt


@dataclasses.dataclass
class Node:
    prev_node: Optional[Node]
    data: bytes
    hash: bytes

    @classmethod
    def build(cls, key: bytes, data: bytes, prev_node: Optional[Node] = None) -> Node:
        prev_hash = b"" if prev_node is None else prev_node.hash
        hash = encrypt(_pad(key, 32), data, _pad(prev_hash, 16))[1]
        return cls(prev_node, data, hash)

    def __iter__(self) -> Iterator[Node]:
        node: Optional[Node] = self
        while node is not None:
            yield node
            node = node.prev_node


def _pad(bytes_: bytes, length: int) -> bytes:
    return bytes_.ljust(length, b"\x00")[:length]


@dataclasses.dataclass
class Chain:
    node: Node

    def add_node(self, key: bytes, data: bytes) -> None:
        self.node = Node.build(key, data, self.node)

    def __len__(self) -> int:
        return sum(1 for _ in self.node)


def main() -> None:
    chain = Chain(Node.build(b"the key", b"here is a bit of data"))
    chain.add_node(b"P@$w0rd", b"and here is a bit more data")
    chain.add_node(b"hunter2", b"and finally here is some more")
    print(chain)


if __name__ == "__main__":
    main()
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/239632

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档