区块链笔记 2020-08-07 笔记,实验 暂无评论 970 次阅读 [TOC] # 一个区块链例程 ## 例程源码 源码来源:[Learn Blockchains by Building One](https://github.com/dvf/blockchain) ``` import hashlib import json from time import time from urllib.parse import urlparse from uuid import uuid4 import requests from flask import Flask, jsonify, request class Blockchain: def __init__(self): self.current_transactions = [] self.chain = [] self.nodes = set() # Create the genesis block self.new_block(previous_hash='1', proof=100) def register_node(self, address): """ Add a new node to the list of nodes :param address: Address of node. Eg. 'http://192.168.0.5:5000' """ parsed_url = urlparse(address) if parsed_url.netloc: self.nodes.add(parsed_url.netloc) elif parsed_url.path: # Accepts an URL without scheme like '192.168.0.5:5000'. self.nodes.add(parsed_url.path) else: raise ValueError('Invalid URL') def valid_chain(self, chain): """ Determine if a given blockchain is valid :param chain: A blockchain :return: True if valid, False if not """ last_block = chain[0] current_index = 1 while current_index < len(chain): block = chain[current_index] print(f'{last_block}') print(f'{block}') print("\n-----------\n") # Check that the hash of the block is correct last_block_hash = self.hash(last_block) if block['previous_hash'] != last_block_hash: return False # Check that the Proof of Work is correct if not self.valid_proof(last_block['proof'], block['proof'], last_block_hash): return False last_block = block current_index += 1 return True def resolve_conflicts(self): """ This is our consensus algorithm, it resolves conflicts by replacing our chain with the longest one in the network. :return: True if our chain was replaced, False if not """ neighbours = self.nodes new_chain = None # We're only looking for chains longer than ours max_length = len(self.chain) # Grab and verify the chains from all the nodes in our network for node in neighbours: response = requests.get(f'http://{node}/chain') if response.status_code == 200: length = response.json()['length'] chain = response.json()['chain'] # Check if the length is longer and the chain is valid if length > max_length and self.valid_chain(chain): max_length = length new_chain = chain # Replace our chain if we discovered a new, valid chain longer than ours if new_chain: self.chain = new_chain return True return False def new_block(self, proof, previous_hash): """ Create a new Block in the Blockchain :param proof: The proof given by the Proof of Work algorithm :param previous_hash: Hash of previous Block :return: New Block """ block = { 'index': len(self.chain) + 1, 'timestamp': time(), 'transactions': self.current_transactions, 'proof': proof, 'previous_hash': previous_hash or self.hash(self.chain[-1]), } # Reset the current list of transactions self.current_transactions = [] self.chain.append(block) return block def new_transaction(self, sender, recipient, amount): """ Creates a new transaction to go into the next mined Block :param sender: Address of the Sender :param recipient: Address of the Recipient :param amount: Amount :return: The index of the Block that will hold this transaction """ self.current_transactions.append({ 'sender': sender, 'recipient': recipient, 'amount': amount, }) return self.last_block['index'] + 1 @property def last_block(self): return self.chain[-1] @staticmethod def hash(block): """ Creates a SHA-256 hash of a Block :param block: Block """ # We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes block_string = json.dumps(block, sort_keys=True).encode() return hashlib.sha256(block_string).hexdigest() def proof_of_work(self, last_block): """ Simple Proof of Work Algorithm: - Find a number p' such that hash(pp') contains leading 4 zeroes - Where p is the previous proof, and p' is the new proof :param last_block: last Block :return: """ last_proof = last_block['proof'] last_hash = self.hash(last_block) proof = 0 while self.valid_proof(last_proof, proof, last_hash) is False: proof += 1 return proof @staticmethod def valid_proof(last_proof, proof, last_hash): """ Validates the Proof :param last_proof: Previous Proof :param proof: Current Proof :param last_hash: The hash of the Previous Block :return: True if correct, False if not. """ guess = f'{last_proof}{proof}{last_hash}'.encode() guess_hash = hashlib.sha256(guess).hexdigest() return guess_hash[:4] == "0000" # Instantiate the Node app = Flask(__name__) # Generate a globally unique address for this node node_identifier = str(uuid4()).replace('-', '') # Instantiate the Blockchain blockchain = Blockchain() @app.route('/mine', methods=['GET']) def mine(): # We run the proof of work algorithm to get the next proof... last_block = blockchain.last_block proof = blockchain.proof_of_work(last_block) # We must receive a reward for finding the proof. # The sender is "0" to signify that this node has mined a new coin. blockchain.new_transaction( sender="0", recipient=node_identifier, amount=1, ) # Forge the new Block by adding it to the chain previous_hash = blockchain.hash(last_block) block = blockchain.new_block(proof, previous_hash) 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('/transactions/new', methods=['POST']) def new_transaction(): values = request.get_json() # Check that the required fields are in the POST'ed data required = ['sender', 'recipient', 'amount'] if not all(k in values for k in required): return 'Missing values', 400 # Create a new Transaction index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount']) response = {'message': f'Transaction will be added to Block {index}'} return jsonify(response), 201 @app.route('/chain', methods=['GET']) def full_chain(): response = { 'chain': blockchain.chain, 'length': len(blockchain.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: blockchain.register_node(node) response = { 'message': 'New nodes have been added', 'total_nodes': list(blockchain.nodes), } return jsonify(response), 201 @app.route('/nodes/resolve', methods=['GET']) def consensus(): replaced = blockchain.resolve_conflicts() if replaced: response = { 'message': 'Our chain was replaced', 'new_chain': blockchain.chain } else: response = { 'message': 'Our chain is authoritative', 'chain': blockchain.chain } return jsonify(response), 200 if __name__ == '__main__': from argparse import ArgumentParser parser = ArgumentParser() parser.add_argument('-p', '--port', default=5000, type=int, help='port to listen on') args = parser.parse_args() port = args.port app.run(host='0.0.0.0', port=port) ``` ## 例程结构 例程包含两部分: 1.实现区块链功能的Blockchain类。 2.基于Flask框架的WebAPI服务。 - Blockchain类 - 成员变量: - current_transactions = [] - 当前的交易(账本) - 当新区块产生后,此账本清空。 - chain = [] - 区块的“链” - nodes = set() - 节点集 - 等广播的时候,是往节点集里的地址广播。 - 用set()不用[],可以避免重复。 - 成员方法: - register_node(address) - 往节点集加入新节点。 - valid_chain(chain) - 检查一个区块的“链”是否有效。 - resolve_conflicts() - 解决冲突。(共识算法) - 把自己(本节点)的“链”替换成网络里最长的链。 - new_block(proof, previous_hash) - 往链里创建一个新区块 - new_transaction(sender, recipient, amount) - 往账本里添加一条交易。 - Getter(property) - last_block() 获取上一个区块 - 静态方法(staticmethod) - hash(block) - 计算一个区块的SHA-256哈希值 - proof_of_work(last_block) - 一个简单的工作量证明算法 - 找到一个数字p',来让 hash(pp') 以4个0开头,其中p是前一个证明,p'是新证明 - valid_proof(last_proof, proof, last_hash) - 检验工作量证明是否有效 - WebAPI - /mine - 运行proof_of_work,找新的p'。 - 也就是开始挖矿。 - 回应: ``` response = { 'message': "New Block Forged", 'index': block['index'], 'transactions': block['transactions'], 'proof': block['proof'], 'previous_hash': block['previous_hash'], } ``` - /transactions/new - 新加一条交易记录。 - Post输入Json: - sender - recipient - amount - 回应: ``` response = {'message': f'Transaction will be added to Block {index}'} ``` - /chain - 获取区块链全链 - 回应: ``` response = { 'chain': blockchain.chain, 'length': len(blockchain.chain), } ``` - /nodes/register - 节点注册 - Post输入Json: - nodes : 节点列表 - 回应: ``` response = { 'message': 'New nodes have been added', 'total_nodes': list(blockchain.nodes), } ``` - /nodes/resolve - 解决冲突(把本地链换成网上的最长链) - 回应: ``` if replaced: response = { 'message': 'Our chain was replaced', 'new_chain': blockchain.chain } else: response = { 'message': 'Our chain is authoritative', 'chain': blockchain.chain } ``` ## 运行之前有几个问题 ### 区块链的存储格式 和我想象不一样,例程中的区块链不存储本地文件。 例程会创建一个Blockchain对象,区块链就存储到这个对象的成员变量里。 参考:[Python对象的组成和内存、引用的本质](https://www.cnblogs.com/yingxiongguixing/p/12163743.html) >变量位于栈内存,对象位于堆内存。 这有两个问题: 1. 内存只能临时存储,程序一关掉,数据就没了。 - 例程的做法: - 每次打开程序,重新建立一个创世区块。 - 等接入其他节点,解决一下冲突,就行了。 - 我的想法: - 感觉这才叫“去中心化”。 - 这好像对“防篡改”没有帮助。 - 改文件比改内存容易。 - 但改的都是本地的区块链。 - 本地自己改的区块链不容易被别的节点认可:要么哈希值不对,区块链无效;要么链不够长。除非51%攻击。 - 起码得有一个长久的节点。要不然哪天所有节点全没上线,数据就真没了。 2. 内存空间太小,迟早会装不下完整的区块链。 - 例程这点可以改进 - 大型的区块链会有中心化的“全节点”和“矿池”。 - 用户节点不需要下载完整的区块链,而是直接去“全节点”查询想要的数据。 - 用户挖矿不是人人都从0开始挖,而是由矿池分配任务区间。 ### 能不能防止双重支付 WebAPI层面,只是检查“发送者”、“接受者”、“数额”这三个参数齐不齐,齐的话直接交给Blockchain对象全权处理。 Blockchain也不检查用户余额,直接把WebAPI给的三个参数写进账本。 ### 能不能防止冒充别的用户 用户账号是结点标识符,相当于公钥。 没有私钥。 所以不能防止冒充。 ### 当产生新区块的时候,有广播吗 没有,但想加很容易:挖到新区块以后,给其他节点都发一个“/nodes/resolve”的HTTP Get请求,其他节点就会解决冲突,自然会换成带有新区块的最长链。 其他节点在替换成新链以后,要对账本里的交易记录做一遍检查,看看每条交易是不是属于双重支付,如果是就拿掉。 有一个矿工挖到新区块以后,所有矿工都要从头开始挖下一轮矿。 ## 安全问题的思考 感觉区块链,区块生得越快,越安全。怕就怕有黑客弄个由垃圾信息组成的链,比主链还长,那么之前记录的数据就都被垃圾信息替换掉了。 所以第一点,**主链**要不断生成新区块,比**黑客链**生长得快,让黑客撵不上。 第二点,解决冲突的时候要多一个心眼,看看**最长链**从头到尾的哈希值,与大多数**隔壁链**的一不一样。想51%攻击就得有51%个节点用**黑客链**。 第三点,长期在线的节点越多,越安全。但要对新节点的加入设置限制,**只能允许受信任的节点加入**。不然的话,我的区块链平时只有三五个节点在线,黑客一台电脑开1w个节点加进来,轻易实现51%攻击。 ## 改进方向 1. 把区块链存入本地数据库,在记账的时候检查用户余额,以防止双重支付。 2. 请求区块链全链、验证区块链是否有效、解决冲突,由处理全链,改为分段处理。这是为了解决内存装不下全链的问题。 3. 支付的时候,除了“发送者”,“接受者”,“数额”以外,还要有“发送者”用私钥生成的“签名”,如果签名解不开,就不记账;签名也要记录到账本里。这样才能防止冒充。 ## 运行例程 ### 本地创建三个节点 新建run.bat并运行,内容为: ``` start blockchain.py -p 5000 start blockchain.py -p 5001 start blockchain.py -p 5002 ``` 在本地创建三个节点。地址为: http://127.0.0.1:5000 http://127.0.0.1:5001 http://127.0.0.1:5002 然后就可以先瞄一眼创世区块是什么样的。 直接访问:http://127.0.0.1:5000/chain 返回: ``` { "chain": [{ "index": 1, "previous_hash": "1", "proof": 100, "timestamp": 1596956116.1584706, "transactions": [] }], "length": 1 } ``` ### 编写界面 例程是后端。用一下午时间给它写一个简单的界面。 为了方便,我在一个服务器上放三个节点,之后直接打开网页就能测试了。 #### 跨域问题 在调试的时候,发现访问WebAPI会出现跨域问题。 ``` Access to XMLHttpRequest at 'http://127.0.0.1:5000/nodes/register' from origin 'https://www.proup.club' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. ``` 解决办法: 在`app.run(host='0.0.0.0', port=port)`之前加入这两行: ``` from flask_cors import CORS CORS(app, supports_credentials=True) ``` #### 证书问题 https的网页,只能访问https的WebAPI,无法访问http的。 ``` Mixed Content: The page at 'https://www.proup.club/index.php/archives/403/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://cs.proup.club:26100/chain'. This request has been blocked; the content must be served over HTTPS. ``` 解决办法是给WebAPI也弄个SSL证书。 把`app.run(host='0.0.0.0', port=port)`改成: ``` app.run(host='0.0.0.0', port=port,ssl_context=('证书.pem的位置','证书.key的位置')) ``` 之后要把“解决冲突”时的http也改成https。 最终的界面可以点击[测试区块链](https://www.proup.club/index.php/archives/403/)使用。 # 测试区块链例程 服务器上运行了三个节点,分别叫做:节点0,节点1,节点2。 三个节点全部重启,恢复到最初的状态,测试开始。 ## 最初状态 依次点击节点链接,再点“获取链”,节点的全链就会显示在“回应”栏。 ``` 节点0:{ "chain": [ { "index": 1, "previous_hash": "1", "proof": 100, "timestamp": 1596977578.3508298, "transactions": [] } ], "length": 1 } ``` ``` 节点1:{ "chain": [ { "index": 1, "previous_hash": "1", "proof": 100, "timestamp": 1596977577.7570798, "transactions": [] } ], "length": 1 } ``` ``` 节点2:{ "chain": [ { "index": 1, "previous_hash": "1", "proof": 100, "timestamp": 1596977577.7570798, "transactions": [] } ], "length": 1 } ``` 此时每个区块链都只有一个区块,这个区块也称为“创世块”。 ## 节点连接 点击“注册节点”,让三个节点两两连接。 ``` 节点0回应:{ "message": "New nodes have been added", "total_nodes": [ "cs.proup.club:26101", "cs.proup.club:26102" ] } ``` ``` 节点1回应:{ "message": "New nodes have been added", "total_nodes": [ "cs.proup.club:26100", "cs.proup.club:26102" ] } ``` ``` 节点2回应:{ "message": "New nodes have been added", "total_nodes": [ "cs.proup.club:26100", "cs.proup.club:26101" ] } ``` ## 挖矿 生成新区块即挖矿。 0号节点挖两次矿,回应如下: ``` 挖第一次回应:{ "index": 2, "message": "New Block Forged", "previous_hash": "1a322565545c7e9db6fda5b4dfa7cfc1ba4dfd6b2aafe3b364b56575c39f2663", "proof": 28072, "transactions": [ { "amount": 1, "recipient": "aa8b74f3eb1a41f2a76d5618f056291a", "sender": "0" } ] } ``` ``` 挖第二次回应:{ "index": 3, "message": "New Block Forged", "previous_hash": "716a2b8153aa341fe9668d6867da20407835e8018dae12a1ca591b4e3eeabeb1", "proof": 126014, "transactions": [ { "amount": 1, "recipient": "aa8b74f3eb1a41f2a76d5618f056291a", "sender": "0" } ] } ``` 值得注意的三点: 1. index 新区块的序号 - 创世区块是1号块 - 第一次挖矿挖出2号块 - 第二次挖矿挖出3号块 2. proof 工作量证明 - 挖矿就是找这个随机数。 - 模拟一下找3号块的工作量证明126014的过程: 1. 首先要知道2号块 ``` { "index": 2, "previous_hash": "1a322565545c7e9db6fda5b4dfa7cfc1ba4dfd6b2aafe3b364b56575c39f2663", "proof": 28072, "timestamp": 1596977796.0539548, "transactions": [ { "amount": 1, "recipient": "aa8b74f3eb1a41f2a76d5618f056291a", "sender": "0" } ] } ``` 2. 从2号块提取以下信息: - 2号块的proof:28072 - 这是挖出2号块的工作量证明 - 2号块的SHA256哈希值:716a2b8153aa341fe9668d6867da20407835e8018dae12a1ca591b4e3eeabeb1 - 第一步:把2号块转为json格式,键(Key)升序排序,得到json字符串: ``` {"index": 2, "previous_hash": "1a322565545c7e9db6fda5b4dfa7cfc1ba4dfd6b2aafe3b364b56575c39f2663", "proof": 28072, "timestamp": 1596977796.0539548, "transactions": [{"amount": 1, "recipient": "aa8b74f3eb1a41f2a76d5618f056291a", "sender": "0"}]} ``` - 第二步:对json字符串进行SHA256运算([在线SHA256加密](http://www.jsons.cn/sha/)),得到哈希值: ``` 716a2b8153aa341fe9668d6867da20407835e8018dae12a1ca591b4e3eeabeb1 ``` 3. 找一个随机数,把“2号块的proof,随机数,2号块的哈希值”拼成的字符串,进行SHA256运算,直到运算结果的前4位是0。 - ``` 28072随机数716a2b8153aa341fe9668d6867da20407835e8018dae12a1ca591b4e3eeabeb1 ``` - 当随机数是126014时,即对 ``` 28072126014716a2b8153aa341fe9668d6867da20407835e8018dae12a1ca591b4e3eeabeb1 ``` 进行SHA256运算([在线SHA256加密](http://www.jsons.cn/sha/)) 能得到计算结果: ``` 00008722fae7f7d3deb343dd29a658e447c99c676df5ef3c9dfb37cf9bdeb042 ``` 高4位是0,挖矿成功。 3. transactions 交易记录(账本) - 账本里最少有一条交易记录,也就是给这个节点(矿工)的挖矿奖励。 - 发送者:0 - 每个区块的第一条交易记录,发送者0才有效。 - 接受者:`aa8b74f3eb1a41f2a76d5618f056291a` - 这是节点0的标识符(钱包地址)(公钥) 让节点1、节点2也挖一个矿,得到每个节点的公钥: 节点0:`aa8b74f3eb1a41f2a76d5618f056291a` 节点1:`c1390e9b64aa4636b7baaffbf60207ac` 节点2:`18d0bf03393a4b7ca7f1bee1ff2d6b54` ## 添加交易 此时三个节点的状态: 节点0:有3个区块,没有交易记录,只有2个挖矿奖励。 节点1:有2个区块,没有交易记录,只有1个挖矿奖励。 节点2:有2个区块,没有交易记录,只有1个挖矿奖励。 ### 模拟节点0和节点2的币都给节点1: #### 如果只有交易双方记账 **第一步:添加交易记录** - 给节点0添加交易记录: - 节点0 → 节点1 : 2币 - 节点0回复:"Transaction will be added to Block 4" - 给节点1添加交易记录: - 节点0 → 节点1 : 2币 - 节点1回复:"Transaction will be added to Block 3" - 节点2 → 节点1 : 1币 - 节点1回复:"Transaction will be added to Block 3" - 给节点2添加交易记录: - 节点2 → 节点1 : 1币 - 节点2回复:"Transaction will be added to Block 3" 此时,交易记录全都在各节点的“账本”里,而不在“链”里。直到挖出新区块。 **第二步:挖矿** 现在让**节点0**和**节点1**各挖一矿,**节点2**不挖矿。 ``` 节点0回复:{ "index": 4, "message": "New Block Forged", "previous_hash": "04600f2b974dbee2a333d6852ad0f98fbd799e0c0ac25904134b898d9f7395fe", "proof": 34153, "transactions": [ { "amount": "2", "recipient": "c1390e9b64aa4636b7baaffbf60207ac", "sender": "aa8b74f3eb1a41f2a76d5618f056291a" }, { "amount": 1, "recipient": "aa8b74f3eb1a41f2a76d5618f056291a", "sender": "0" } ] } ``` ``` 节点1回复:{ "index": 3, "message": "New Block Forged", "previous_hash": "5959600191a873d49ceef1edf55c9a68376cab8a2e65490fa674401b8f9d0279", "proof": 127810, "transactions": [ { "amount": "2", "recipient": "c1390e9b64aa4636b7baaffbf60207ac", "sender": "aa8b74f3eb1a41f2a76d5618f056291a" }, { "amount": "1", "recipient": "c1390e9b64aa4636b7baaffbf60207ac", "sender": "18d0bf03393a4b7ca7f1bee1ff2d6b54" }, { "amount": 1, "recipient": "c1390e9b64aa4636b7baaffbf60207ac", "sender": "0" } ] } ``` 发现两个问题: 1. 交易记录"transactions"为什么是往前加的,常规想法是往后面加。 - 节点0给节点1依次发送1,2,3个币 - 结果:`[1币交易,2币交易,3币交易,挖矿奖励]`。 - 挖矿奖励是在即将挖矿的时候才加进去的,而不是挖出新区块后才加的。 - 挖矿奖励就是账本里的**最后一条**交易记录。 2. 挖矿奖励的"amount"是数值,交易的"amount"是字符串。 - 写网页前端的时候忘了处理了。 - `parseInt(str);` string转整数 - `parseFloat(str);` string转小数 **第三步:解决冲突** **此时三个链的状态:** - 节点0:长度5,记录有: - 节点0 :4次挖矿奖励(第2,3,4,5块) - 节点0 → 节点1 : 2币(第4块) - 节点0 → 节点1 : 1、2、3、4币(第5块) - 节点1:长度为4,记录有: - 节点1 :3次挖矿奖励(第2,3,4块) - 节点0 → 节点1 : 2币(第3块) - 节点2 → 节点1 : 1币(第3块) - 节点0 → 节点1 :1、2、3、4币(第4块) - 节点2:长度为2,记录有: - 节点2 : 1次挖矿奖励(第2块) - *注:节点2 → 节点1 : 1币 的记录,在账本里,不在链里。 按照节点0、节点1、节点2的顺序,分别解决冲突。 **让节点0解决冲突:** 节点0的账本是空的,长度也最长,节点0解决冲突应该不会改变任何节点。 节点0回应消息:`Our chain is authoritative` 节点0、节点1、节点2的链确实没变化。 **让节点1解决冲突:** 节点1回应消息:`Our chain was replaced` 节点1的链被替换成了节点0的链。 - 对节点1来说,有2条记录没了: - 节点1 :3次挖矿奖励(第2,3,4块) - 节点2 → 节点1 : 1币(第3块) **让节点2解决冲突:** 节点2回应消息:`Our chain was replaced` 节点2的链被替换成了节点0的链。 让节点2挖一个矿,此时节点2的状态: - 节点2:长度6,记录有: - 节点0 :4次挖矿奖励(第2,3,4,5块) - 节点0 → 节点1 : 2币(第4块) - 节点0 → 节点1 : 1、2、3、4币(第5块) - 节点2 :1次挖矿奖励(第6块) - 节点2 → 节点1 :1币(第6块) - 对节点2来说,有1条记录没了 - 节点2 : 1次挖矿奖励(第2块) 此时让节点0和节点1解决冲突。 节点0和节点1都替换成了节点2的链。 - 对节点1和节点2来说,有一条记录回来了: - 节点2 → 节点1 :1币(第6块) #### 思考 只有交易双方记账,交易记录是有可能丢失的。 节点1的交易记录丢失的主要原因,我认为是一点:**没先解决冲突,就开始挖矿**。所以挖完矿以后,一个解决冲突,就把自己的链给更新没了。 而节点2,没急着挖矿,而是先解决冲突,把自己的链弄成网上的最长链。虽然自己之前的链没了,但交易记录完好得保存在账本里。这时候挖矿,把账本变成区块,此时自己的链是最长的,通知节点0和节点2解决冲突,节点2与节点1的交易记录就记录到区块链里了。 因此,一个安全的操作顺序是: ① 解决冲突。 ② 挖矿。 ③ 通知别的区块解决冲突。 按照这个顺序,一个交易只有交易双方记账,是可行的。但这样有好有坏: - 好处: - 一次交易,需要通知的节点个数很少。 - 可能的“记账手续费”也会少。 - 通信时间也会少。 - 只要双方有一方挖到矿,记录即可存入主链。 - 坏处: - 如果矿很难挖,记录就很难入链。 - 从交易到入链,之间的时间会拖很长 - 挖到矿之前,必须保存好账本内容。 - 账本不保存成本地文件的话,重启就会消失。 - 要考虑到可能的停电、死机等故障。 - 如果双方的节点都撑不到入链,不如广播给更多节点。 - 即便只有两个人记账,也避免不了双重支付的问题。 - 比如双方运气好,各挖到一个矿,仍然要考虑双重支付的问题。 - 只有一个人记账,才能避免双重支付问题,但这有点不现实。 标签: 区块链 本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
评论已关闭