As I was streaming I had a brilliant visitor suggest we write a blockchain in Python.
So we did.
Note that there is no validation or voting simulated here.
Here's the results, in 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()
And here's a tiny test, 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
Note that we did require oscrypto and openssl to run this.
Ran coverage with pytest under Python 3.7. Ran with black and mypy as well.
"for string literals, and would change your inline comment to be PEP 8 compliant. Also it would add 2 empty lines between your top level functions and classes. \$\endgroup\$