摘要:首先,區(qū)塊鏈?zhǔn)且幌盗蟹Q作區(qū)塊的結(jié)構(gòu)順序鏈接而成的不可改變的記錄。表示一個區(qū)塊鏈我們創(chuàng)建一個類,其構(gòu)造器會創(chuàng)建兩個列表,一個存儲區(qū)塊鏈,另一個存儲交易。我們幾乎完成了表示一個區(qū)塊鏈的工作。
目前大多數(shù)對于區(qū)塊鏈的文章都是停留在概念性的描述,大肆宣揚(yáng)其顛覆性,本文則反其道行之,以一個程序員的視角,通過300行代碼,快速實(shí)現(xiàn)了一個區(qū)塊鏈原型。雖然沒有覆蓋區(qū)塊鏈的全部內(nèi)容(如Merkle樹),但對于理解區(qū)塊鏈的核心技術(shù)仍大有裨益。
----譯者注
能夠點(diǎn)進(jìn)這篇文章,說明你也像我一樣對加密貨幣的興起十分激動,并想了解加密貨幣的支撐技術(shù)---區(qū)塊鏈?zhǔn)侨绾喂ぷ鞯摹?br>但理解區(qū)塊鏈并不那么輕松,至少對我來說如此。我看了很多相關(guān)的視頻和教程,卻沮喪地發(fā)現(xiàn)實(shí)例真是太少了。
我喜歡通過實(shí)踐學(xué)習(xí)。這種方式使我在代碼層面思考問題,并發(fā)現(xiàn)關(guān)鍵所在。如果你和我一樣,那么在本文結(jié)尾你將構(gòu)建一個功能完備的區(qū)塊鏈并對其工作機(jī)制有深刻的理解。
首先,區(qū)塊鏈?zhǔn)且幌盗蟹Q作區(qū)塊(Block)的結(jié)構(gòu)順序鏈接而成的不可改變的記錄。塊中可以包含交易記錄、文件或者其他任何你想存儲的數(shù)據(jù)。需要注意的是塊與塊之間通過hash值鏈接。如果你不清楚hash是什么,請參考What Are Hash Functions。
本文適合哪些人看?你應(yīng)該懂得一些基本的Python知識,同時也應(yīng)該對HTTP請求有所理解,因?yàn)槲覀兊膮^(qū)塊鏈?zhǔn)沁\(yùn)行在HTTP協(xié)議之上的。
我需要準(zhǔn)備什么?請確保Python3.6及以上版本和pip工具已經(jīng)安裝。還需要安裝Flask和requests庫。
pip install Flask==0.12.2 requests==2.18.4
對了,你還需要一個HTTP客戶端,比如Postman或者cURL,當(dāng)然,其他的也可以。
最終的代碼哪里可以獲?。?/strong>點(diǎn)擊這里
第一步:構(gòu)建區(qū)塊鏈創(chuàng)建一個新的Python文件,名為blockchain.py,我們所有的邏輯都在一個文件完成。
表示一個區(qū)塊鏈我們創(chuàng)建一個BlockChain類,其構(gòu)造器會創(chuàng)建兩個列表,一個存儲區(qū)塊鏈,另一個存儲交易。下面是我們這個類的第一個版本:
class Blockchain(object): def __init__(self): self.chain = [] self.current_transactions = [] def new_block(self): # Creates a new Block and adds it to the chain pass def new_transaction(self): # Adds a new transaction to the list of transactions pass @staticmethod def hash(block): # Hashes a Block pass @property def last_block(self): # Returns the last Block in the chain pass
我們的BlockChain類負(fù)責(zé)管理整個區(qū)塊鏈,它會存儲交易并為新增區(qū)塊等操作提供輔助方法。下面,我們來實(shí)現(xiàn)這些方法。
區(qū)塊是什么每個區(qū)塊都有一個索引(index),一個時間戳(timestamp),一系列交易,一個工作量證明(稍后詳述)和前置區(qū)塊的哈希值。下面是單個區(qū)塊的一個簡單實(shí)例:
block = { "index": 1, "timestamp": 1506057125.900785, "transactions": [ { "sender": "8527147fe1f5426f9dd545de4b27ee00", "recipient": "a77f5cdfa2934df3954a5c7c7da5df1f", "amount": 5, } ], "proof": 324984774000, "previous_hash": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" }
顯而易見,所有的區(qū)塊會構(gòu)成一條鏈---因?yàn)槊總€區(qū)塊都保存了前一區(qū)塊的hash值。這就是區(qū)塊鏈不可篡改的重要原因:如果攻擊者損壞了某一區(qū)塊,那么后面所有的區(qū)塊都會作廢。
如果你不明白上面的話,請花一些時間理解,因?yàn)檫@是區(qū)塊鏈的核心思想。
我們需要一個方法來向區(qū)塊中添加交易記錄,這里命名為new_transaction(),代碼寫的十分直白易懂:
class Blockchain(object): ... 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
在new_transaction()方法將交易添加進(jìn)區(qū)塊之后,區(qū)塊索引將會被返回,該區(qū)塊將可能被開采為鏈的最新區(qū)塊,這在之后用戶提交交易的時候十分有用。
創(chuàng)建新區(qū)塊當(dāng)BlockChain類初始化的時候,我們需要產(chǎn)生一個創(chuàng)世區(qū)塊(genesis block,即沒有前置區(qū)塊的區(qū)塊)作為區(qū)塊鏈的第一個區(qū)塊。我們還需要添加一個proof字段在創(chuàng)世區(qū)塊中作為挖礦的結(jié)果(或者說本次工作量的證明),我們將在后文繼續(xù)討論挖礦。
除了產(chǎn)生創(chuàng)世區(qū)塊,我們還需要完成一些其他輔助方法(new_block(),new_transaction()和hash()):
import hashlib import json from time import time class Blockchain(object): def __init__(self): self.current_transactions = [] self.chain = [] # Create the genesis block self.new_block(previous_hash=1, proof=100) def new_block(self, proof, previous_hash=None): """ Create a new Block in the Blockchain :param proof:The proof given by the Proof of Work algorithm :param previous_hash: (Optional) 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 :return: """ # 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()
上面的代碼十分直白,我還添加了一些注釋幫助理解。我們幾乎完成了表示一個區(qū)塊鏈的工作。但此時,你應(yīng)該思考下一個區(qū)塊是如何產(chǎn)生或者說被開采出來的。
理解工作量證明機(jī)制(Proof of Work)工作量證明(PoW)算法是用來產(chǎn)生或開采區(qū)塊的一種機(jī)制,PoW的目標(biāo)是找到一個符合要求的數(shù)字,從算力的角度來說這個數(shù)字對任何人來說都很難找到卻十分容易驗(yàn)證(是否符合要求)。這就是PoW算法的核心思想。
我們舉一個非常簡單的例子來幫助理解:
假定我們需要找到一個整數(shù)y,使得他和整數(shù)x的乘積的哈希值以0結(jié)尾,即hash(x*y) = ac23dc...0。如果x=5那么用Python實(shí)現(xiàn)如下:
from hashlib import sha256 x = 5 y = 0 # We don"t know what y should be yet... while sha256(f"{x*y}".encode()).hexdigest()[-1] != "0": y += 1 print(f"The solution is y = {y}")
第一個符合要求的數(shù)是y=21,因?yàn)椋?/p>
hash(5 * 21) = 1253e9373e...5e3600155e860
在比特幣世界中,PoW算法被稱為哈?,F(xiàn)金(Hashcash),它和我們上面的例子沒有本質(zhì)區(qū)別。在這算法中,礦工們開始了解決問題的競賽,優(yōu)勝者可以產(chǎn)生一個新的區(qū)塊。通常來說,難度取決于限制字符的數(shù)量。礦工將會因?yàn)檎业揭粋€合法的解答收到一些比特幣作為獎勵,整個比特幣網(wǎng)絡(luò)能夠很容易驗(yàn)證礦工挖掘的區(qū)塊是否合法有效。
實(shí)現(xiàn)基本的PoW算法下面來為我們的區(qū)塊鏈實(shí)現(xiàn)一個類似的算法,我們的規(guī)則將會和上面的例子十分接近:找到一個數(shù)p,使得它與前置區(qū)塊的哈希值由4個0開頭。
import hashlib import json from time import time from uuid import uuid4 class Blockchain(object): ... def proof_of_work(self, last_proof): """ Simple Proof of Work Algorithm: - Find a number p" such that hash(pp") contains leading 4 zeroes, where p is the previous p" - p is the previous proof, and p" is the new proof :param last_proof::return: """ proof = 0 while self.valid_proof(last_proof, proof) is False: proof += 1 return proof @staticmethod def valid_proof(last_proof, proof): """ Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes? :param last_proof: Previous Proof :param proof: Current Proof :return: True if correct, False if not. """ guess = f"{last_proof}{proof}".encode() guess_hash = hashlib.sha256(guess).hexdigest() return guess_hash[:4] == "0000"
我們可以通過設(shè)置前導(dǎo)0的個數(shù)調(diào)整算法的難度,但4個足夠了,你會發(fā)現(xiàn)增加一個0會使找到一個答案的時間大大增加。我們的類幾乎完成了,現(xiàn)在我們將通過HTTP請求與區(qū)塊鏈進(jìn)行交互。
第二步:將區(qū)塊鏈作為API我們將使用Flask,它是一個輕量級的框架,可以很容易將一個網(wǎng)絡(luò)節(jié)點(diǎn)映射為Python函數(shù),這讓我們可以通過HTTP請求與區(qū)塊鏈交互。我們將創(chuàng)建以下方法:
/transactions/new建立一個新的區(qū)塊。
/mine告訴服務(wù)器開采一個新的區(qū)塊
/chain返回整個區(qū)塊鏈
設(shè)置Flask我們的每個服務(wù)器將對應(yīng)區(qū)塊鏈網(wǎng)絡(luò)中的一個單一節(jié)點(diǎn)。下面是樣板代碼:
import hashlib import json from textwrap import dedent from time import time from uuid import uuid4 from flask import Flask class Blockchain(object): ... # Instantiate our 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(): return "We"ll mine a new Block" @app.route("/transactions/new", methods=["POST"]) def new_transaction(): return "We"ll add a new transaction" @app.route("/chain", methods=["GET"]) def full_chain(): response = { "chain": blockchain.chain, "length": len(blockchain.chain), } return jsonify(response), 200 if __name__ == "__main__": app.run(host="0.0.0.0", port=5000)
下面是簡單的解釋:
15行:實(shí)例化節(jié)點(diǎn),關(guān)于Flask點(diǎn)擊Quick Start
18行:為節(jié)點(diǎn)創(chuàng)建一個隨機(jī)名字
21行:實(shí)例化BlockChain類
24-26行:創(chuàng)建/mine節(jié)點(diǎn),這是一個GET請求。
28-30行:創(chuàng)建/transactions/new節(jié)點(diǎn),因?yàn)樾枰l(fā)送數(shù)據(jù),所以是POST請求。
32-38行:創(chuàng)建/chain節(jié)點(diǎn),返回整個區(qū)塊鏈
40-41行:運(yùn)行服務(wù)器5000端口
交易節(jié)點(diǎn)用戶會想服務(wù)器發(fā)送交易請求,格式類似下面這樣:
{ "sender": "my address", "recipient": "someone else"s address", "amount": 5 }
因?yàn)槲覀円呀?jīng)實(shí)現(xiàn)了將交易加入?yún)^(qū)塊的方法,所以剩余部分十分容易:
import hashlib import json from textwrap import dedent from time import time from uuid import uuid4 from flask import Flask, jsonify, request ... @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挖礦節(jié)點(diǎn)
挖礦節(jié)點(diǎn)很簡單但也很神奇,他需要完成以下任務(wù):
計算執(zhí)行PoW算法
通過添加一筆交易獎勵礦工1比特幣
產(chǎn)生新的區(qū)塊并添加入鏈
import hashlib import json from time import time from uuid import uuid4 from flask import Flask, jsonify, request ... @app.route("/mine", methods=["GET"]) def mine(): # We run the proof of work algorithm to get the next proof... last_block = blockchain.last_block last_proof = last_block["proof"] proof = blockchain.proof_of_work(last_proof) # 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
需要注意的是接受被開采區(qū)塊的地址就是我們的節(jié)點(diǎn),并且我們的大部分工作就是和BlockChain類的方法交互。我們已經(jīng)完成了這部分,現(xiàn)在可以開始和區(qū)塊鏈交互了。
第三步:和區(qū)塊鏈交互你可以使用簡單古老的cURL或者Postman來和這些網(wǎng)絡(luò)中的API交互,首先啟動服務(wù)器:
$ python blockchain.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
讓我們發(fā)送一個GET請求來開采一個區(qū)塊:
http://localhost:5000/mine
再向http://localhost:5000/transactions/new發(fā)送一個POST請求,參數(shù)是JSON格式的交易數(shù)據(jù):
如果不想用Postman,cURL也可以做到:
$ curl -X POST -H "Content-Type: application/json" -d "{ "sender": "d4ee26eee15148ee92c6cd394edd974e", "recipient": "someone-other-address", "amount": 5 }" "http://localhost:5000/transactions/new"
我重啟了服務(wù)器并開采了兩個區(qū)塊,所以現(xiàn)在總共有3個了,通過http://localhost:5000/chain節(jié)點(diǎn)可以獲取整個區(qū)塊:
{ "chain": [ { "index": 1, "previous_hash": 1, "proof": 100, "timestamp": 1506280650.770839, "transactions": [] }, { "index": 2, "previous_hash": "c099bc...bfb7", "proof": 35293, "timestamp": 1506280664.717925, "transactions": [ { "amount": 1, "recipient": "8bbcb347e0634905b0cac7955bae152b", "sender": "0" } ] }, { "index": 3, "previous_hash": "eff91a...10f2", "proof": 35089, "timestamp": 1506280666.1086972, "transactions": [ { "amount": 1, "recipient": "8bbcb347e0634905b0cac7955bae152b", "sender": "0" } ] } ], "length": 3 }第四步:共識機(jī)制
我們已經(jīng)擁有了一個能接收交易的初級區(qū)塊鏈,并且能夠開采出新的區(qū)塊。但整個區(qū)塊鏈最核心的是去中心化,如果去中心了,我們又如何保證所有節(jié)點(diǎn)對應(yīng)的是統(tǒng)一區(qū)塊鏈呢?這就是共識問題,如果我們希望網(wǎng)絡(luò)中有不止一個節(jié)點(diǎn),就必須實(shí)現(xiàn)共識算法。
注冊新節(jié)點(diǎn)在實(shí)現(xiàn)共識算法之前,我們需要讓節(jié)點(diǎn)知道有其他節(jié)點(diǎn)加入了網(wǎng)絡(luò)。網(wǎng)絡(luò)中的每一個節(jié)點(diǎn)應(yīng)該存留其他全部節(jié)點(diǎn)的注冊表,因此我們需要一些其他的服務(wù)器節(jié)點(diǎn):
/nodes/register用來從URL中接收一系列節(jié)點(diǎn)
/nodes/resolve實(shí)現(xiàn)共識算法,并解決沖突以保證節(jié)點(diǎn)擁有正確的鏈
我們需要修改BlockChain類的構(gòu)造器并提供一個方法來注冊節(jié)點(diǎn):
... from urllib.parse import urlparse ... class Blockchain(object): def __init__(self): ... self.nodes = set() ... 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" :return: None """ parsed_url = urlparse(address) self.nodes.add(parsed_url.netloc)
現(xiàn)在可以使用set()來存儲節(jié)點(diǎn)列表。這保證了節(jié)點(diǎn)的添加是冪等的,即一個節(jié)點(diǎn)無論添加多少次只會出現(xiàn)一次。
實(shí)現(xiàn)共識算法當(dāng)一個節(jié)點(diǎn)的區(qū)塊鏈和另一節(jié)點(diǎn)的區(qū)塊鏈不同時,沖突就發(fā)生了。為了解決這個問題,我們需要制定規(guī)則:最長有效鏈最有權(quán)威性,即網(wǎng)絡(luò)中最長的那條鏈?zhǔn)钦嬲膮^(qū)塊鏈。使用這個算法,我們能夠達(dá)成大多數(shù)節(jié)點(diǎn)的一致。
... import requests class Blockchain(object) ... 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(" ----------- ") # Check that the hash of the block is correct if block["previous_hash"] != self.hash(last_block): return False # Check that the Proof of Work is correct if not self.valid_proof(last_block["proof"], block["proof"]): 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
valid_chain()通過遍歷每一區(qū)塊并檢查proof和hash的正確與否判斷鏈的有效性。resolve_conflicts()遍歷所有節(jié)點(diǎn),并通過上面的方法驗(yàn)證其有效性。如果一個有效鏈的長度大于當(dāng)前鏈,那么當(dāng)前鏈將會被替換?,F(xiàn)在添加兩個API端口,一個用來添加節(jié)點(diǎn),一個用來解決沖突:
@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
現(xiàn)在你可以用不同的計算機(jī)來構(gòu)建網(wǎng)絡(luò)中的這些節(jié)點(diǎn),當(dāng)然也可以用同一機(jī)器的不同端口。例如,將5001端口也注冊進(jìn)區(qū)塊鏈網(wǎng)絡(luò):
現(xiàn)在,如果我在第二個節(jié)點(diǎn)開采一個新的區(qū)塊,當(dāng)我在節(jié)點(diǎn)1調(diào)用GET /nodes/resolve的時候,共識算法會保證鏈被更新到現(xiàn)有網(wǎng)絡(luò)中的最長鏈:
現(xiàn)在你可以找一些朋友來和你一起測試這個區(qū)塊鏈了。
后記我希望這篇文章能夠激發(fā)你的靈感,畢竟我對加密貨幣十分狂熱,我相信他會改變我們對金融、政府和記錄存儲的思考方式。
Update:我計劃寫這個話題的第二部分,我將進(jìn)一步拓展這個區(qū)塊鏈并支持交易驗(yàn)證( Transaction Validation Mechanism),同時也將討論如何將你的區(qū)塊鏈產(chǎn)業(yè)化。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/44700.html
摘要:當(dāng)你在學(xué)習(xí)了解概念的時候,必定會產(chǎn)生很多疑問,例如我們經(jīng)??梢钥吹揭痪浔忍貛诺墓沧R機(jī)制是通過工作量證明來實(shí)現(xiàn)的。這時又有了新的疑問,比特幣在驗(yàn)證什么樣的結(jié)果,這時你又需要了解密碼學(xué)和。 本文首發(fā)于深入淺出區(qū)塊鏈社區(qū)原文鏈接:前言-如何學(xué)習(xí)區(qū)塊鏈原文已更新,請讀者前往原文閱讀 區(qū)塊鏈未來3到5年應(yīng)該會出現(xiàn)行業(yè)井噴式發(fā)展,相應(yīng)所需的人才必定水漲船高,每一個開發(fā)人員都不應(yīng)該錯過這樣的機(jī)會。區(qū)...
摘要:我同時也建立了一個基于瀏覽器的版本安裝命令行工具在此之前請先安裝然后在你的命令行中運(yùn)行以下指令你應(yīng)該會看到和一個提示。 原文:How does blockchain really work? I built an app to show you.作者:Sean Han譯者:JeLewine 根據(jù)維基百科,區(qū)塊鏈?zhǔn)牵?一個用于維護(hù)不斷增長的記錄列表的分布式數(shù)據(jù)庫,我們稱之為區(qū)塊鏈。 這聽...
摘要:我同時也建立了一個基于瀏覽器的版本安裝命令行工具在此之前請先安裝然后在你的命令行中運(yùn)行以下指令你應(yīng)該會看到和一個提示。 原文:How does blockchain really work? I built an app to show you.作者:Sean Han譯者:JeLewine 根據(jù)維基百科,區(qū)塊鏈?zhǔn)牵?一個用于維護(hù)不斷增長的記錄列表的分布式數(shù)據(jù)庫,我們稱之為區(qū)塊鏈。 這聽...
摘要:作者原文第一部分應(yīng)用混沌工程理論到區(qū)塊鏈框架。你可以抗議混沌環(huán)境在像與這種權(quán)限不足的公共區(qū)塊鏈網(wǎng)絡(luò)上是否存在。在之后這些被稱之為混沌工程?;煦缭瓌t開始進(jìn)入正式規(guī)范。名字是混沌工程通過實(shí)驗(yàn)建立對系統(tǒng)行為的信心。 作者 Vipin Bharathan原文:https://medium.com/@vipinsun/... 第一部分. 應(yīng)用混沌工程理論到區(qū)塊鏈框架。 混沌與工程兩個字是沒有什么...
閱讀 1852·2021-08-19 11:12
閱讀 1426·2021-07-25 21:37
閱讀 990·2019-08-30 14:07
閱讀 1268·2019-08-30 13:12
閱讀 653·2019-08-30 11:00
閱讀 3530·2019-08-29 16:28
閱讀 994·2019-08-29 15:33
閱讀 2969·2019-08-26 13:40