Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

JavaScript开发区块链只需200行代码

ranbluer
65 0 0
JavaScript开发一个简单的区块链只需200行代码。通过JavaScript的开发实现过程,你将理解区块链是什么:区块链就是一个分布式数据库,存储结构是一个不断增长的链表,链表中包含着许多有序的记录。
7 a" p" u9 P$ ^" ~然而,在通常情况下,当我们谈到区块链的时候也会谈起使用区块链来解决的问题,这两者很容易混淆。
6 C1 S. ~3 V5 x+ c( M像流行的比特币和以太坊这样基于区块链的项目就是这样。“区块链”这个术语通常和像交易、智能合约、加密货币这样的概念紧紧联系在一起。+ c) A4 v: P) d& b9 E
这就令理解区块链变得不必要得复杂起来,特别是当你想理解源码的时候。下面我将通过 200 行 JS 实现的超级简单的区块链来帮助大家理解它,我给这段代码起名为 NaiveChain。你可以在Github(https://github.com/lhartikk/naivechain)查看更多的技术细节。
- F& u1 C$ F$ j+ O0 H# ~/ M3 L! x块结构/ ~7 o% W5 m  a8 R. d
第一个逻辑步骤是决定块结构。为了保证事情尽可能的简单,我们只选择最必要的部分:index(下标)、timestamp(时间戳)、data(数据)、hash(哈希值)和 previous hash(前置哈希值)。4 c2 C: t' u6 t% Z+ o- T0 d

% P  H/ F  S4 G8 ?这个块中必须能找到前一个块的哈希值,以此来保证整条链的完整性,JavaScript代码如下:; ]! K! ?7 {* a# g( H( g
class Block {' x4 ]' D$ L9 y& K/ E
    constructor(index, previousHash, timestamp, data, hash) {5 x, o7 [9 @+ L2 w9 P
        this.index = index;
- Q# m* j' W0 d3 x1 ?- a  O) K        this.previousHash = previousHash.toString();
* @  w+ e/ I1 D. C9 |& |; s6 f. ~        this.timestamp = timestamp;
1 Z, A. T* {7 q4 B  ~        this.data = data;) `5 h3 }. f4 e; y
        this.hash = hash.toString();  f2 z8 g# e% V& e
    }
$ G: j, h" o% o}! `# C+ w. g' S! d" u$ H! K+ b
块哈希, O  A4 `2 l0 h+ P. W1 t3 R3 c5 P
为了保存完整的数据,必须哈希区块。SHA-256会对块的内容进行加密,记录这个值应该和“挖矿”毫无关系,因为这里不需要解决工作量证明的问题。JavaScript代码如下:
3 |1 C& w0 E* W$ X0 uvar calculateHash = (index, previousHash, timestamp, data) => {
4 Y: @8 b9 x! Z; V9 f9 A    return CryptoJS.SHA256(index + previousHash + timestamp + data).toString();
, `- v, P% F6 c; c: Z};5 l! ^/ F% k1 u2 \  ^) j
块的生成% {) c8 z: S( a; G8 w2 ?; x
要生成一个块,必须知道前一个块的哈希值,然后创造其余所需的内容(= index, hash, data and timestamp)。
" S. E9 D- {3 P3 }" U块的data部分是由终端用户所提供的。JavaScript代码如下:( D9 B: F: o8 K4 n6 A8 ]# `
var generateNextBlock = (blockData) => {+ Y& a; n7 ^; w$ `: M
    var previousBlock = getLatestBlock();# N: g4 c6 h/ o3 y
    var nextIndex = previousBlock.index + 1;
6 D7 V6 x& c0 \0 k" W* p$ a8 Q; q    var nextTimestamp = new Date().getTime() / 1000;
  z( S% T( N& p* K6 c+ V    var nextHash = calculateHash(nextIndex, previousBlock.hash, nextTimestamp, blockData);4 m8 W. O! F! [5 L1 Z
    return new Block(nextIndex, previousBlock.hash, nextTimestamp, blockData, nextHash);( J" V* @8 M3 W# l  a
};
6 f4 |' Y- [7 @! p# F块的存储/ j/ u# C# V# W; B) i7 z
内存中的Javascript数组被用于存储区块链。区块链的第一个块通常被称为“起源块”,是硬编码的。JavaScript代码如下:/ H/ _6 d  S  W/ J
var getGenesisBlock = () => {: f- n! W, G* `4 w- R) t8 K: U
    return new Block(0, "0", 1465154705, "my genesis block!!", "816534932c2b7154836da6afc367695e6337db8a921823784c14378abed4f7d7");1 Q0 z$ k& Z9 h' w: d2 g: r; i" B
};
. {" a( u! @) a9 a( b " ~+ H; @" S( L. |: D" M
var blockchain = [getGenesisBlock()];
% N9 [/ D# s7 i确认块的完整性- r- M2 Z1 Q; v6 ]
在任何时候都必须能确认一个区块或者一整条链的区块是否完整。在我们从其他节点接收到新的区块,并需要决定接受或拒绝它们时,这一点尤为重要。JavaScript代码如下:
! s! c5 H, |1 N6 k/ X4 ?+ {var isValidNewBlock = (newBlock, previousBlock) => {
/ u" ?% ~& V9 a& G; q( U0 y' E    if (previousBlock.index + 1 !== newBlock.index) {6 u" ^' K. X( T  Q, Q+ ~
        console.log('invalid index');
$ M/ g! F7 R8 f, ^3 p        return false;
  Y& [$ }4 }! I    } else if (previousBlock.hash !== newBlock.previousHash) {  l9 L9 S" x# u+ K4 j/ V/ o
        console.log('invalid previoushash');
2 D5 s1 I+ |+ p$ s9 `% o* p, V( Q) `        return false;6 I  [# W) U, c0 K& e0 a1 Y
    } else if (calculateHashForBlock(newBlock) !== newBlock.hash) {4 ~! X  e, U( q6 F0 F, B* F1 b8 q
        console.log('invalid hash: ' + calculateHashForBlock(newBlock) + ' ' + newBlock.hash);
+ w+ `. |- a  \- [7 V        return false;
& U- q  h9 X$ ?8 B: z1 ^4 W) D" r! Z    }
# l7 m* Y$ W3 J5 O- E' W    return true;
  h( ~- \1 ?! h( e3 ?% R% M7 P6 z) Q! x& a/ V};
$ s0 w: b) G6 g  G; M% U$ C选择最长的区块链3 Q5 v; y' C* E( i& Q5 h

3 e0 m+ G( A, f! ?) h/ g任何时候在链中都应该只有一组明确的块。万一冲突了(例如:两个结点都生成了72号块时),会选择有最大数目的块的链。JavaScript代码如下:1 i* j/ m+ `5 A; N, ?, U+ I
var replaceChain = (newBlocks) => {
" ]0 z4 l5 z3 }4 f    if (isValidChain(newBlocks) && newBlocks.length > blockchain.length) {
  \/ s+ ^; z7 `" g1 t        console.log('Received blockchain is valid. Replacing current blockchain with received blockchain');) b% K4 l; a. J" ?
        blockchain = newBlocks;
  a2 b4 k+ o8 y0 H2 s        broadcast(responseLatestMsg());; J% ~  |0 q, Z# u
    } else {
+ Y$ ?* h. t. S# a  R+ v. S. V        console.log('Received blockchain invalid');2 x+ _7 l5 d- w! Z' {- F5 h. a, H, D
    }
0 N- X  \2 G2 L};
6 p3 O8 E+ E4 z) S0 r与其他结点的通信
$ D" p/ Z! d( U8 q) b$ k% `结点的本质是和其他结点共享和同步区块链,下面的规则能保证网络同步。
; X- [+ M" Z) S. i$ s+ }# z1 [
& q# N. F; @" y当一个结点生成一个新块时,它会在网络上散布这个块。- A$ @: v6 b* m: a$ z. K
当一个节点连接新peer时,它会查询最新的block。
2 I9 K0 g0 c' d/ j当一个结点遇到一个块,其index大于当前所有块的index时,它会添加这个块到它当前的链中,  或者到整个区块链中查询这个块。
; {- }3 l) {* g4 y8 \我没有采用自动发现peer的工具。peers的位置(URL)必须是手动添加的。* Y  f0 ^7 A3 |& k6 U
节点控制
; a4 `* S! h1 r# Q% n: y( t: q在某种程度上用户必须能够控制节点。这一点通过搭建一个HTTP服务器可以实现。JavaScript代码如下:
9 ^, Q+ S  l* u# g% |' q; e  N1 r  a) ovar initHttpServer = () => {) ^/ u8 t- t" J
    var app = express();5 g2 x8 g7 i. G; W2 N) F4 s* l
    app.use(bodyParser.json());
) R# @$ N& ?$ B9 y+ V1 A9 i    app.get('/blocks', (req, res) => res.send(JSON.stringify(blockchain)));2 M6 U9 N) ?( ?9 G7 D6 W
    app.post('/mineBlock', (req, res) => {
! [- Z6 t6 z+ c/ X, g1 ^# V2 [        var newBlock = generateNextBlock(req.body.data);
. s# b; N# {6 Y  T7 {$ I1 P! V        addBlock(newBlock);
" y" I- c* g: O! U8 D        broadcast(responseLatestMsg());
. E5 ^) |/ ^$ n        console.log('block added: ' + JSON.stringify(newBlock));
, x. C! ?; }! B: G0 {        res.send();6 U1 v. Z$ N% f! y
    });
5 p* c5 U! Y/ d! S1 G* Q5 C    app.get('/peers', (req, res) => {: ]( L( c$ u9 |3 c3 ]( G5 H  ~
        res.send(sockets.map(s => s._socket.remoteAddress + ':' + s._socket.remotePort));
& y( x: N/ K; N6 o! M2 H    });9 s5 Y% ?" V2 Y7 u
    app.post('/addPeer', (req, res) => {
/ N; b$ H; T8 d; H        connectToPeers([req.body.peer]);8 i# U5 z! [% s: c, }/ {$ T5 Y0 {! p. v
        res.send();% \; E  w, c/ U  W, W# N
    });
; _: P  I9 B: a. v    app.listen(http_port, () => console.log('Listening http on port: ' + http_port));. J- m; {$ u+ ]/ ~
};
1 r0 i' V, [- {; s, ^# c用户可以用下面的方法和节点互动:2 I& b0 r& ^) j
列出所有的块用户提供的内容创建一个新的块列出或者新增peers7 w1 }/ K  o0 `4 A6 p

6 q, Q$ ~4 w: i6 r下面这个Curl的例子就是最直接的控制节点的方法:# f+ D! g! T. K  o' P  \5 x
#get all blocks from the node, a2 C0 J. x* A* v
curl http://localhost:3001/blocks, T, W# y# U  u* [9 W
系统架构
" c6 a) Q: t) Q0 d$ t8 y0 ^* a2 q需要指出的是,节点实际上展现了两个web服务器:一个(HTTP服务器)是让用户控制节点,另一个(Websocket HTTP服务器)。
8 x7 a  C, D5 N2 s, m
2 |( U8 m' ~" Z0 v# r& `4 E总结4 Z: i; m- `* S8 i; i* ~# T7 z
创造 NaiveChain 的目的是为了示范和学习,因为它并没有“挖矿”算法(PoS或PoW),不能被用于公用网络,但是它实现了区块链运作的基本特性。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

ranbluer 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    1