区块链中每个块包含一个头部和一个正文:
这些是每个块具有的数据。除此之外,每个块都有一个由开发人员定义的最大字节数。达到此最大大小时,此块将添加到区块链中,并开始创建新区块。
块号BlockNumber
区块链中的每个区块都有一个代表区块在整个链中位置的数字,链中的第一个块有一个特殊的名称,它们被称为genesis块,也就是起始块,创世块。
前一块号PrevBlockHash
每个块在头部中都有前一个块哈希号的引用,这非常重要,因为这就是区块链保持一致并控制不变性的方式,创世块没有任何引用前一个块的内容,因为它是第一个块。
MerkleRoot
merkleRoot是块内所有事务的散列结果,如果在块内部更改了任何内容,则此哈希值也会更改并使更改的块和下一个块无效。
现在你可以想想:为什么这个字段被称为MerkleRoot而不是blockHash?这是因为比特币使用Merkle树算法从块的数据生成哈希值的。
nonce
这是一个与块的挖掘过程相关的32位数字。此数字仅用于查找与区块链难度相匹配的哈希值
CreationTimestamp
这是块创建的时间戳。
数据
这是所有交易数据的保留位置。
哈希
哈希算法实际上取决于区块链的每个实现,比特币使用长度为256位的SHA-256(安全哈希算法)。
区块链
区块链通过prevBlockHash将块链接在一起:
merkleRoot值是从Data信息生成的,如果有任何变化,merkleRoot也会改变,并且在更改的块之后链接prevBlockHash的其余块将不再有效。这就是区块链不可变的原因。
工作证明
工作证明对于区块链是一个重要特征,而且是必须的;区块链中其他经常被提及特征(如安全性)反而是次要的,虽有用但非必须。
任何分类账都绝对需要顺序。一个人不能花没有收到的钱,也不能花已经花了的钱。区块链交易(或称区块链事务)必须明确顺序,并且不需要可信任的第三方来协调顺序,这就是前面讲的分布式事务机制。如果交易是由世界各地的匿名参与者产生的,并且没有中心化组织负责交易之间的顺序排列(不同于集中式的分布式事务数据库),但事实确实需要一个排序,那么该怎么办呢?虽然一个交易(或块)可能包括时间戳,但这些时间戳怎么可信?
在分布式系统中不可能将事件与时间点关联起来,这是一个未解决的问题,直到中本聪发明了区块链的工作证明这个解决方案之后,分散的分类帐才可能得以实现。
区块链的工作证明是一个符合某个要求的SHA-2哈希值,这个值是非常难以找到的。困难之处在于哈希小于一个特定数字,数字越小,输入值越稀少并且发现它的难度就越高。它被称为“工作证明”,就是因为已知具有这种哈希的值已经非常罕见,这意味着找到新的这样的值需要大量的试错,即“工作”。反过来,这意味着 消耗"时间"。比特币寻找难度是动态调整的,这样每十分钟平均能找到一个正确的哈希值。
也就是说,区块链没有办法决定谁最快算出哈希值(因为每个参与者的服务器时钟不可能像对手表那样对得精确),那么延后一段时间比如10分钟,谁先算出谁获胜,然后迅速繁衍,谁的链越长越有优势,这是对于10分钟内可能有两个获胜者的附加判断条件
用Javasctipt代码简单模拟解释区块链概念
本文试图用简单200行代码来解析区块链的基本原理。
区块链基本概念很简单:它其实是一个分布式数据库,维护一个不断增长的有序的记录列表。“区块链”术语通常与诸如交易,智能合约或加密货币等概念密切相关。涉及这么多概念使得理解区块链变成一个艰巨的任务。这里将通过一个称为NaiveChain库包使用200行的Javascript实现超简单的blockchain。
块结构
第一个逻辑步骤是决定块结构。为了使事情尽可能简单,我们仅包含最必要的:index,timestamp,data,hash和previous hash。
必须在块中找到前一个块的Hash,以保持链的完整性,代码如下:
class Block { constructor(index, previousHash, timestamp, data, hash) { this.index = index; this.previousHash = previousHash.toString(); this.timestamp = timestamp; this.data = data; this.hash = hash.toString(); } }
块Hash散列
块需要实行Hash以保持数据的完整性。算法使用SHA-256。应该注意,这个Hash与“ 挖掘 ” 无关,因为不需要解决Proof Of Work工作证明问题。
var calculateHash = (index, previousHash, timestamp, data) => { return CryptoJS.SHA256(index + previousHash + timestamp + data).toString(); };
生成块
为了生成一个块,我们必须知道前一个块的哈希,并创建所需内容的其余部分(=索引,散列,数据和时间戳),块数据是由最终用户提供的。
var generateNextBlock = (blockData) => { var previousBlock = getLatestBlock(); var nextIndex = previousBlock.index + 1; var nextTimestamp = new Date().getTime() / 1000; var nextHash = calculateHash(nextIndex, previousBlock.hash, nextTimestamp, blockData); return new Block(nextIndex, previousBlock.hash, nextTimestamp, blockData, nextHash); };
存储块
内存中的Javascript数组用于存储区块链。区块链的第一块总是一个所谓的“基因块genesis-block”,它是硬编码的。
var getGenesisBlock = () =>{ return new Block(0,"0",1465154705,"my genesis block!!", "816534932c2b7154836da6afc367695e6337db8a921823784c14378abed4f7d7"); }; var blockchain=[getGenesisBlock()];
验证块的完整性
在任何指定时间,我们必须能够验证块或块链在完整性方面是否有效。特别是当我们从其他节点接收新块,并必须决定是否接受他们时。
var isValidNewBlock = (newBlock, previousBlock) => { if (previousBlock.index + 1 !== newBlock.index) { console.log('invalid index'); return false; } else if (previousBlock.hash !== newBlock.previousHash) { console.log('invalid previoushash'); return false; } else if (calculateHashForBlock(newBlock) !== newBlock.hash) { console.log('invalid hash: ' + calculateHashForBlock(newBlock) + ' ' + newBlock.hash); return false; } return true; };
选择最长的链
在指定时间链中应该总是只有一个显式块。在冲突的情况下(例如,两个节点都生成块号72),我们选择具有最长块数的链。
var replaceChain = (newBlocks) => { if (isValidChain(newBlocks) && newBlocks.length > blockchain.length) { console.log('Received blockchain is valid. Replacing current blockchain with received blockchain'); blockchain = newBlocks; broadcast(responseLatestMsg()); } else { console.log('Received blockchain invalid'); } };
与其他节点通信
节点的一个重要部分是与其他节点共享和同步区块链。以下规则用于保持网络同步。
- 当节点生成新块时,它将其广播到网络
- 当节点连接到新的对等体时,它查询最新的块
- 当节点遇到具有大于当前已知块的索引的块时,它将块添加到其当前链或查询整个区块链。
上图是当节点服从所描述的协议时遵循的一些典型的通信场景
不使用自动对等体发现。对等体的位置(= URL)就必须手动添加。
控制节点
用户必须能够以某种方式控制节点。这是通过设置HTTP服务器来完成的。
var initHttpServer = () = > { var app = express(); app.use(bodyParser.json()); app.get('/blocks', (req, res) = > res.send(JSON.stringify(blockchain)) ) ; app.post('/mineBlock', (req, res) = > { var newBlock = generateNextBlock(req.body.data); addBlock(newBlock); broadcast(responseLatestMsg()); console.log('block added: ' + JSON.stringify(newBlock)); res.send(); }); app.get('/peers', (req, res) = > { res.send(sockets.map(s = > s._socket.remoteAddress + ':' + s._socket.remotePort) ); }); app.post('/addPeer', (req, res) = > { connectToPeers([req.body.peer]); res.send(); }); app.listen(http_port, () = > console.log('Listening http on port: ' + http_port) ); } ;
如代码所示,用户能够以以下方式与节点交互:
- 列出所有块
- 创建由用户提供内容的新块
- 列出或添加对等体
控制节点的最直接的方法是例如使用Curl:
#get所有节点的块
curl http://localhost:3001/blocks
架构
应该注意,一个节点实际上暴露了两个Web服务器:一个用于用户控制节点(HTTP服务器),一个用于节点之间的对等通信(Websocket HTTP服务器)
NaiveChain的主要组件
结论
NaiveChain是为了演示和学习区块链而创建的。由于它没有“ 挖掘 ”算法(PoS的PoW),它不能在公共网络中使用。它仍然实现了功能区块链的基本特征。
您可以检查Github存储库以获取更多技术详细信息。