commit 2bf2e0f1cf08685c9d5f63d13f40db45fda67a04 Author: qyx <565485304@qq.com> Date: Mon Jul 3 15:43:57 2023 +0800 init project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9bc15c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,184 @@ +### VirtualEnv template +# Virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ +.Python +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts +pyvenv.cfg +.venv +pip-selfcheck.json + +### Example user template template +### Example user template + +# IntelliJ project files +.idea +*.iml +out +gen +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + diff --git a/README.md b/README.md new file mode 100644 index 0000000..eca54cf --- /dev/null +++ b/README.md @@ -0,0 +1,130 @@ +## 1. 区块链技术的核心概念和原理 +- 密码朋克 (Cypher punk) + - 维基解密的创始人 阿桑奇 + - BT下载的作者 布莱姆-科恩 + - WWW 的发明者 蒂姆博纳斯-李 + - 智能合约概念的提出者: 尼克萨博 + - Facebook 创始人: 肖恩帕克 + - 中本聪 + +- Adam Back 发明了 Hash cash, 使用了 POW +- Haber/ Stornetta 提出时间戳方法保证数字文件安全的协议 +- 戴维 发明了 B-money, 强调了点对点交易和不可更改记录 +- 哈尔-芬尼 推出了 "加密现金" +- 2008 年中本聪 《比特币: 一个点对点的电子现金系统》 + +- 区块链技术前景 + - 应用场景 + - 资产:数字资产发行, 支付(跨境支付), 交易, 结算 + - 记账:股权交易, 供应链金融, 商业积分 + - 不可篡改:溯源, 众筹, 医疗证明, 存在性证明 + - 点对点:共享经济, 物联网 + - 隐私: 匿名交易 + +- 什么是货币? + - 凯恩斯《货币论》货币是可以承载价值的一般等价物 + +- 货币历史 + - 铜币, 金银 -> 银票 -> 法币 -> 数字货币(一串数字) + +- 信任从何而来 + - 财产只受自己控制 + - 无通胀(总量不变得) + - 没有假钞 + - 流通性好 + +- 比特币是什么 + - 去中心化的记账系统 + +- 比特币的原理 + - 账本如何验证? + - 所有权问题? + - 为什么记账?(挖矿) + - 以谁的账本为准?(共识机制) + +- Hash + - 哈希函数: Hash(原始信息) = 摘要信息 + - 特点: + - 同样的原始信息用同一个哈希函数总能得到相同的摘要信息 + - 原始信息任何微小的变化都会哈希出面目全非的摘要信息 + - 从摘要信息无法逆向推算出原始信息 + +- 非对称加密技术(交易签名) + - 交易进行hash得到摘要 + - 用私钥对摘要进行签名 + +- 所有权问题? 广播交易 + - 签名及验证 + +- 为什么记账? + - 记账: Hash 打包过程 + - 消耗资源 + - 奖励 -> 比特币发行 + +- 挖矿 - 工作量证明 + - 规则: 一段时间内只有一人可以记账成功 + - 通过解决密码学的难题(即工作量证明)竞争获得唯一记账权 + - 其他的节点复制记账结果 + +- 工作量证明 + - Hash(上一个Hash值, 交易记录集) = 456635BCD + - Hash(上一个Hash值, 交易记录集, 随机数) = 0000aFD635BCD + +- 交易记录集 + - 收集广播中还没有被记录账本的交易 + - 交易的有效性验证 + - 添加一笔给自己转账的交易(挖矿奖励) + +- 共识机制 + - 两个节点同时完成工作量证明, 使用谁的区块? + - 无仲裁机构裁决 + - 为什么要遵守协议? + - 节点的工作量只有在其他节点认可认同其是有效地 + - 累计工作量最大的区块链 + - 独立 + - 延长最长链 + +## 2. 区块链的原理实现 (Python3) + +- python, pip, pipenv, Flask/requests + +```shell +# 安装运行的环境 +pip install pipenv +# 安装 python 的运行环境 -> 生成 Pipfile +pipenv --python=python3.6 +# 安装需要的依赖 +pipenv install flask==0.12.2 +pipenv install requests==2.18.4 +``` + +### 2.1 创建项目及区块结构 +- blockchain.py + + + + + + + + + + + + + + + + + + + + + + + + + + + +## \ No newline at end of file diff --git a/demo/blockchain.py b/demo/blockchain.py new file mode 100644 index 0000000..84721a7 --- /dev/null +++ b/demo/blockchain.py @@ -0,0 +1,279 @@ +# -*- coding: utf-8 -*- +import hashlib +import json +from argparse import ArgumentParser +from time import time +from urllib.parse import urlparse +from uuid import uuid4 + +import requests +from flask import Flask, jsonify, request + + +# 区块的结构 +# { +# # 序号 +# "index": 0, +# # 时间戳 +# "timestamp": "", +# # 交易信息 +# "transactions": [ +# { +# # 交易的发送者 +# "sender": "", +# # 交易的接收者 +# "recipient": "", +# # 交易的金额 +# "amount": 5 +# } +# +# ], +# # 工作量证明 +# "proof": "", +# # 上一个区块的 Hash值 +# "previous_hash": "" +# } + +class Blockchain: + + # 初始化 + def __init__(self): + self.chain = [] + self.current_transactions = [] + # 保存节点的信息 + self.nodes = set() + + # 创建一个创世纪的区块定义 + self.new_block(proof=100, previous_hash=1) + + # 验证链为有效链 -> Hash值和工作量证明是否有效 + def valid_chain(self, chain) -> bool: + last_block = chain[0] + current_index = 1 + + while current_index < len(chain): + block = chain[current_index] + + # Hash 不一致, 则校验不通过, 是虚假的信息 + if block['previous_hash'] != self.hash(last_block): + return False + + # 工作量证明可能不满足, 不满足 "0000"开头的 + if not self.valid_proof(last_block['proof'], block['proof']): + return False + + last_block = block + current_index += 1 + return True + + # 共识机制 解决多个节点的冲突问题 + def resolve_conflicts(self) -> bool: + # 使用当前链的长度和附近节点的链的长度进行对比, 选择最长的链进行继续工作 + neighbours = self.nodes + max_length = len(self.chain) + new_chain = None + + for node in neighbours: + 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 + return True + + return False + + # 注册一个节点 + def register_node(self, address: str): + # 解析节点信息 127.0.0.1:5001, 保存到 nodes 里面 + parsed_url = urlparse(address) + self.nodes.add(parsed_url.netloc) + + # 创建新的区块 + def new_block(self, proof, previous_hash=None): + # 创建区块, 把交易信息加入区块 + block = { + 'index': len(self.chain) + 1, + 'timestamp': time(), + 'transactions': self.current_transactions, + 'proof': proof, + 'previous_hash': previous_hash or self.hash(self.last_block) + } + + # 把交易信息清空 + self.current_transactions = [] + # 把区块加入到链中 + self.chain.append(block) + + return block + + # 新添加一个交易 + def new_transaction(self, sender, recipient, amount) -> int: + # 把创建的交易信息作为一个交易记录[json], 放在交易列表里 + self.current_transactions.append( + { + 'sender': sender, + 'recipient': recipient, + 'amount': amount + } + ) + + # 返回区块的索引 + return self.last_block['index'] + 1 + + # (静态) 计算 Hash + @staticmethod + def hash(block): + # json 排序转成 字符串, 然后进行编码 + block_str = json.dumps(block, sort_keys=True).encode() + + # 使用 hash 函数进行 hash, 并返回摘要信息 + return hashlib.sha256(block_str).hexdigest() + + # (属性) 获取最后一个区块 + @property + def last_block(self): + return self.chain[-1] + + # 工作量证明 + def proof_of_work(self, last_proof: int) -> int: + proof = 0 + while self.valid_proof(last_proof, proof) is False: + proof += 1 + + # print(proof) + return proof + + # 验证合规的工作量 比如以几个 0 开头, 这里先定义 4个0 + @staticmethod + def valid_proof(last_proof: int, proof: int) -> bool: + # 字符串拼接并进行编码 -> 构建 guess + guess = f'{last_proof}{proof}'.encode() + # 获取 guess 的 hash 值 + guess_hash = hashlib.sha256(guess).hexdigest() + # print(guess_hash) + + return guess_hash[0:4] == "0000" + + +app = Flask(__name__) +block_chain = Blockchain() + +# UUID 来声明当前节点的ID +node_identifier = str(uuid4()).replace('-', '') + + +# 交易接口 +@app.route('/transactions/new', methods=['POST']) +def new_transaction(): + # 拿到请求内容 + values = request.get_json() + + # 检查请求字段是否完整 + required = ["sender", "recipient", "amount"] + if values is None: + return "Missing values", 400 + if not all(k in values for k in required): + return "Missing values", 400 + + index = block_chain.new_transaction(values['sender'], values['recipient'], values['amount']) + + response = {"message": f'Transcation will be added to Block {index}'} + return jsonify(response), 201 + + +# 挖矿接口 +@app.route('/mine', methods=['GET']) +def mine(): + # 传入上一个工作量证明, 来计算当前的工作量证明 + last_block = block_chain.last_block + last_proof = last_block['proof'] + proof = block_chain.proof_of_work(last_proof) + + # 新添加一个交易, 这个交易就是给自己的奖励 + block_chain.new_transaction(sender="0", recipient=node_identifier, amount=1) + + # 新建一个区块 + block = block_chain.new_block(proof, None) + + # 封装返回信息 + response = { + "message": "New Block Forged", + "index": block['index'], + "transactions": block['transactions'], + "proof": block['proof'], + "previous_hash": block['previous_hash'] + } + return jsonify(response), 200 + + +# 返回整个区块链信息 +@app.route('/chain', methods=['GET']) +def full_chain(): + response = { + 'chain': block_chain.chain, + 'length': len(block_chain.chain) + } + return jsonify(response), 200 + + +# 添加一个节点 +@app.route('/nodes/register', methods=['POST']) +def register_nodes(): + # 获取节点信息 + values = request.get_json() + nodes = values.get("nodes") + + if nodes is None: + return "Error: please supply a valid list of nodes", 400 + + for node in nodes: + block_chain.register_node(node) + + response = { + "message": "New nodes have bean added", + "total_nodes": list(block_chain.nodes) + } + + return jsonify(response), 201 + + +# 共识机制, 解决冲突的接口 +@app.route('/nodes/resolve', methods=['GET']) +def consensus(): + replaced = block_chain.resolve_conflicts() + + if replaced: + response = { + 'message': 'Our chain was replaced', + 'new_chain': block_chain.chain + } + else: + response = { + 'message': 'Our chain is authoritative', + 'chain': block_chain.chain + } + return jsonify(response), 200 + + +if __name__ == '__main__': + # 测试一下 工作量证明 + # test_Pow = Blockchain() + # test_Pow.proof_of_work(100) + + # 单机模式 + # app.run(host='0.0.0.0', port=5000) + + # 多节点模式 , 通过命令行参数进行实现 + # -p -port + parser = ArgumentParser() + parser.add_argument('-p', '-port', default=5000, type=int, help='port to listen to') + args = parser.parse_args() + port = args.port + + app.run(host='0.0.0.0', port=port)