Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

ranbluer
86 0 0
JavaScript开发一个简单的区块链只需200行代码。通过JavaScript的开发实现过程,你将理解区块链是什么:区块链就是一个分布式数据库,存储结构是一个不断增长的链表,链表中包含着许多有序的记录。; ?  @8 c# b: y& a+ t+ |& N) p; C6 C( u
然而,在通常情况下,当我们谈到区块链的时候也会谈起使用区块链来解决的问题,这两者很容易混淆。2 W' y6 Y% F. |9 k! |- P2 K
像流行的比特币和以太坊这样基于区块链的项目就是这样。“区块链”这个术语通常和像交易、智能合约、加密货币这样的概念紧紧联系在一起。
, O, F6 \5 K$ H% M6 Y- @& j1 P* z这就令理解区块链变得不必要得复杂起来,特别是当你想理解源码的时候。下面我将通过 200 行 JS 实现的超级简单的区块链来帮助大家理解它,我给这段代码起名为 NaiveChain。你可以在Github(https://github.com/lhartikk/naivechain)查看更多的技术细节。' \/ F( q3 ?4 z1 B" U' R
块结构' E) C5 f  Y! w" b" B$ C  ]6 d
第一个逻辑步骤是决定块结构。为了保证事情尽可能的简单,我们只选择最必要的部分:index(下标)、timestamp(时间戳)、data(数据)、hash(哈希值)和 previous hash(前置哈希值)。
8 Q2 v: g! M& S, s4 }! o) \& x& p3 w6 N" z, j
这个块中必须能找到前一个块的哈希值,以此来保证整条链的完整性,JavaScript代码如下:
! y2 {' s* z% j- Sclass Block {
" |4 w. ]% A9 m6 K: }! N/ m; O* S( s    constructor(index, previousHash, timestamp, data, hash) {
9 Z& F4 v8 J2 O2 ^. N        this.index = index;# N/ Q3 o; g+ k& ~. a1 s$ W
        this.previousHash = previousHash.toString();
( t  [* g  ~; h; G, t) K# _1 B        this.timestamp = timestamp;  U7 N* ?! |: H2 x2 W
        this.data = data;- ?# ?8 z1 R  ]+ D8 h% E* }$ z
        this.hash = hash.toString();  ?$ r" V0 p. Q5 L- N' Q" A1 m
    }- S' t9 V" h* J2 d! L* M2 E+ H0 |
}
6 e  T( ~* r2 b5 I/ ^. C* X5 a块哈希
2 X3 @( b  x  y为了保存完整的数据,必须哈希区块。SHA-256会对块的内容进行加密,记录这个值应该和“挖矿”毫无关系,因为这里不需要解决工作量证明的问题。JavaScript代码如下:
% ?3 K* n: Z+ Y) {" Ivar calculateHash = (index, previousHash, timestamp, data) => {+ z( E) t4 D7 f+ d1 I& d
    return CryptoJS.SHA256(index + previousHash + timestamp + data).toString();
) L. z. b7 q3 Y( ?};. a7 l4 ?: u. N- \8 c* H+ H
块的生成
/ I2 W1 o1 _# ^. q# d, N8 p要生成一个块,必须知道前一个块的哈希值,然后创造其余所需的内容(= index, hash, data and timestamp)。. V4 u( L3 J4 B4 K- A' N$ G
块的data部分是由终端用户所提供的。JavaScript代码如下:3 @4 i& L! H( Z* ?
var generateNextBlock = (blockData) => {
& l3 B) @$ ~, {0 R! {0 {/ I    var previousBlock = getLatestBlock();
1 }5 I0 Z2 t7 @& Q! s! C' h' w    var nextIndex = previousBlock.index + 1;9 A' _  v* r& ^$ L: g  N. W; T
    var nextTimestamp = new Date().getTime() / 1000;1 S; o- n, ]( }# h$ I# }0 |
    var nextHash = calculateHash(nextIndex, previousBlock.hash, nextTimestamp, blockData);1 e# ?- C& m! `# c8 G4 E6 w
    return new Block(nextIndex, previousBlock.hash, nextTimestamp, blockData, nextHash);
; q) R/ c: N1 w! S* r};: C3 j4 f5 {+ `, d" Q" F6 C: E
块的存储0 H+ j& W' T7 H, i: i, L& C
内存中的Javascript数组被用于存储区块链。区块链的第一个块通常被称为“起源块”,是硬编码的。JavaScript代码如下:0 m! H  f9 x8 n9 v
var getGenesisBlock = () => {
' t& ?$ s9 v. e# `  _    return new Block(0, "0", 1465154705, "my genesis block!!", "816534932c2b7154836da6afc367695e6337db8a921823784c14378abed4f7d7");. d$ G$ u- c+ `# e  |, u$ F
};) m: ]! w% u; n2 Y

5 m& b7 `* D* ^1 W/ l" {9 zvar blockchain = [getGenesisBlock()];
- }1 O- f9 z7 ^7 @9 ~# B3 w确认块的完整性: N0 g$ z* A2 q$ b8 w: A2 n/ z
在任何时候都必须能确认一个区块或者一整条链的区块是否完整。在我们从其他节点接收到新的区块,并需要决定接受或拒绝它们时,这一点尤为重要。JavaScript代码如下:
& y' |% d: n3 |% ], x! D3 mvar isValidNewBlock = (newBlock, previousBlock) => {
  ~. d5 N) i' T0 }% L+ F2 F8 p9 x    if (previousBlock.index + 1 !== newBlock.index) {
: o- O) f# e) e2 r        console.log('invalid index');
% Q- [* \4 X) a        return false;
+ L0 C1 q, F& [    } else if (previousBlock.hash !== newBlock.previousHash) {
+ {3 z, a0 _1 {) }( e        console.log('invalid previoushash');& d: T: ~2 L7 T% T* u! ]/ H
        return false;9 f% K! o, h0 V5 _, T# F, l  o
    } else if (calculateHashForBlock(newBlock) !== newBlock.hash) {
6 e9 g/ y. w6 q6 {/ ^; u8 m        console.log('invalid hash: ' + calculateHashForBlock(newBlock) + ' ' + newBlock.hash);
  Y/ a- Z% H6 y, M7 c        return false;
% a/ v. G' e' m2 L  c% k6 _    }
8 l, D, x3 Y( H. f8 q    return true;
# Z1 ^6 u* K# G1 l0 k};- c0 w0 j2 P$ n( k; a. z/ h) M. Z
选择最长的区块链' D( l1 f1 g& {7 |
2 t" R: x& X, v) _
任何时候在链中都应该只有一组明确的块。万一冲突了(例如:两个结点都生成了72号块时),会选择有最大数目的块的链。JavaScript代码如下:
4 B4 t; y3 ~1 ^% qvar replaceChain = (newBlocks) => {
7 D& F$ y1 t6 _4 {    if (isValidChain(newBlocks) && newBlocks.length > blockchain.length) {6 |* o" j1 s. a' b: O, z1 g% M8 }
        console.log('Received blockchain is valid. Replacing current blockchain with received blockchain');
; R: B/ {1 d$ D5 b        blockchain = newBlocks;
8 c+ [2 g) f8 B9 o# T        broadcast(responseLatestMsg());
+ c0 J7 k9 b9 K8 B5 v    } else {: H9 A) v' B+ |( m! b
        console.log('Received blockchain invalid');' q/ E  v0 Q+ X4 c+ I* I. f
    }8 A( W1 ]5 e6 |2 W
};; |1 ]+ F( J8 ^. q; j) O. Q2 x- U
与其他结点的通信9 c* c1 B" C: i
结点的本质是和其他结点共享和同步区块链,下面的规则能保证网络同步。$ a$ `1 ~% z0 O# B9 t
% z* {& L, Q, y" D9 Q7 z) H
当一个结点生成一个新块时,它会在网络上散布这个块。
+ m/ q  N+ Z% P7 q! h当一个节点连接新peer时,它会查询最新的block。  J; v8 T/ \4 y; u
当一个结点遇到一个块,其index大于当前所有块的index时,它会添加这个块到它当前的链中,  或者到整个区块链中查询这个块。
# O, D$ ]- m3 y' X  O我没有采用自动发现peer的工具。peers的位置(URL)必须是手动添加的。6 o+ ~  r. \& M4 Q
节点控制
: ~" G$ J3 [+ U. l8 o0 M在某种程度上用户必须能够控制节点。这一点通过搭建一个HTTP服务器可以实现。JavaScript代码如下:' V1 ~% ]% C! m, i
var initHttpServer = () => {, H3 k* {" a) U( L5 r
    var app = express();
7 j8 Y* W7 r# s7 }( j3 `* O) _. r    app.use(bodyParser.json());
/ m) k8 N1 t( \4 z$ }0 e0 f4 Y; y    app.get('/blocks', (req, res) => res.send(JSON.stringify(blockchain)));
; S( N/ D/ s1 G2 C5 y    app.post('/mineBlock', (req, res) => {+ O: {2 X( V- \1 L5 U
        var newBlock = generateNextBlock(req.body.data);
2 \* b" H$ |* K, t        addBlock(newBlock);* e4 C9 E, r$ P# B. B9 X
        broadcast(responseLatestMsg());: m6 q, I  a3 S* Q7 a  ]( G
        console.log('block added: ' + JSON.stringify(newBlock));
" ~$ T# G% N6 X- N: e/ t        res.send();
9 n$ Z& f' x$ Y) S; E* P+ u$ [* Q    });( q  }3 t3 k+ i- |2 t5 B1 Q
    app.get('/peers', (req, res) => {8 \, @% c! z0 L& c- X  Q- G
        res.send(sockets.map(s => s._socket.remoteAddress + ':' + s._socket.remotePort));  U9 k# r' w1 i4 n8 P' [# y$ w
    });& n" d* _* n: R0 ~2 h
    app.post('/addPeer', (req, res) => {5 c) F, S5 [2 r0 y
        connectToPeers([req.body.peer]);
: J8 [9 C  w* C' m        res.send();
3 ]# r$ J6 V: |4 ~, d8 F/ F    });
, b/ O' V/ }5 d! }    app.listen(http_port, () => console.log('Listening http on port: ' + http_port));
* I) d4 B) w: h2 F# K% ^) F; l};! I/ D& W) B1 a) u: u
用户可以用下面的方法和节点互动:) P6 P1 V' Y7 o) t% f0 F6 I( F
列出所有的块用户提供的内容创建一个新的块列出或者新增peers
: [6 x/ Z  ?2 _' ^- c( s* m
0 z3 s* b  L. c
下面这个Curl的例子就是最直接的控制节点的方法:
* e# q, G; T/ [) R6 f4 i4 w4 V#get all blocks from the node, A% C: o6 w4 r4 T$ i! |! n
curl http://localhost:3001/blocks2 N% [$ {, n- j/ K# l8 D* ?
系统架构
: b; M5 |9 `  Z8 @0 ?需要指出的是,节点实际上展现了两个web服务器:一个(HTTP服务器)是让用户控制节点,另一个(Websocket HTTP服务器)。
- g/ Q7 m+ G* j- a3 A3 w
2 W+ j. [" W; }& h/ b; K1 B- e总结
, m% S' u' M; U9 r创造 NaiveChain 的目的是为了示范和学习,因为它并没有“挖矿”算法(PoS或PoW),不能被用于公用网络,但是它实现了区块链运作的基本特性。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

ranbluer 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    1