从本质上看,区块链远没有你想象的那么难!
区块链的基本概念是非常简单的:一个维护不断增长的有序数据的分布式数据库。然而当我们讨论使用区块链解决具体问题的时候,很容易把他们混为一体,比如当前非常有名的比特币和以太坊,它们往往和交易、智能合约、加密货币等概念绑在一起。
这让理解区块链变成了非常困难的事情,但区块链本质上没有这么复杂。Lauri Hartikka使用200行js代码实现一个简单的区块链,称为NaiveChain
区块结构
第一个逻辑步骤就是定义区块结构。为了让这个区块链尽可能的简单,在这个区块链中只包含一些必需元素,包括:区块索引、时间戳、区块数据、当前区块Hash、上一个区块的Hash blockclass 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和挖矿没有关系,因为这里没有工作量证明的问题需要解决。
var calculateHash = (index, previousHash, timestamp, data) => {
return CryptoJS.SHA256(index + previousHash + timestamp + data).toString();
};
生成区块
为了打包一个区块,除了知道上一个区块的hash值以外,还需要得到当前区块的索引、hash、数据,以及当前的时间戳。区块数据有使用者提供。
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);
};
存储区块
在这里使用js的数组将区块存储在内存中。第一个区块通过硬编码完成,通常被称为创始区块。
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的区块),那么当前节点会选择最长链。 longest chainvar 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');
}
};
节点通信
一个区块的必要模块就是同其他节点分享和同步区块链。为了保持区块链在网络中的同步,节点需要遵守以下规则:
-
当一个节点完成打包一个区块之后,要广播到网络
-
当一个节点连接到另一个节点之后,需要查询最新区块
-
当一个节点接收到一个大于当前区块索引的区块之后,需要将这个区块添加到当前区块之后,或者查询整个区块链
sync这里没有节点发现的功能,需要手动添加节点地址
节点管理
通过搭建一个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命令: curl http://localhost:3001/blocks
架构
需要注意的是节点暴露了两个web服务器:一个用户管理当前节点,一个用于区块之间的p2p通信(Websocket HTTP server)
Architecture总结
这个区块链(NaiveChain)是为了演示和学习区块链。它不能在实践中的使用,因为他没有PoS或PoW挖矿算法,然而它实现了区块链的基本特性。你可以通过 Github 仓库获取更多的技术细节。
你可以通过a tutorial for building a cryptocurrency深入理解区块链,在那里你会获得关于工作量证明、交易、钱包等相关知识。
引用
原文链接: https://medium.com/@lhartikk/a-blockchain-in-200-lines-of-code-963cc1cc0e54
本文由 Copernicus团队 戚帅
翻译,转载无需授权。
网友评论