init project
commit
2bf2e0f1cf
@ -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/
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
##
|
@ -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)
|
Loading…
Reference in New Issue