当我是流式传输时,我有一位出色的访问者建议我们用Python编写一个区块链。
我们就这么做了。
请注意,这里没有验证或投票模拟。
以下是package/blockchain.py中的结果:
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:
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运行覆盖率。带着黑色和伤感跑。
发布于 2020-03-30 02:31:13
--strict标志输入Python,而不是混合Python。当您使用该标志并键入所有函数和方法时,您会注意到Node.prior_node存在问题。目前它被指定为Node类型,但是我们知道这是一个谎言,因为我们有if prior_node is None。--ignore-missing-imports,而不是忽略每个导入,因为它们会随着时间的推移迅速积累起来。__repr__是非标准。由瑞尔()内置函数调用,以计算对象的“正式”字符串表示形式。如果可能的话,这应该看起来像一个有效的Python表达式,可以用来重新创建具有相同值的对象(给定适当的环境)。如果这是不可能的,则应该返回表单<...some useful description...>的字符串。您可能想要使用__str__。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'')Node改为数据集。既然你不能写代码,为什么要写呢?这需要将哈希生成移到类方法中。__iter__方法,这样我们就可以轻松地从任何节点遍历链。这使得Chain的D29方法非常简单和干净。_ensure_byte_length改名为_pad。该函数有两个任务,pad是众所周知的,并允许我们有一个更短的功能名称。_ensure_byte_length不需要额外的bytes调用。Chain.add_node是非Pythonic的.在Python中,从带有突变的函数中不返回任何内容是标准的。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()https://codereview.stackexchange.com/questions/239632
复制相似问题