Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

ranbluer
109 0 0
JavaScript开发一个简单的区块链只需200行代码。通过JavaScript的开发实现过程,你将理解区块链是什么:区块链就是一个分布式数据库,存储结构是一个不断增长的链表,链表中包含着许多有序的记录。4 v* O& B. P5 F+ _7 Q5 a
然而,在通常情况下,当我们谈到区块链的时候也会谈起使用区块链来解决的问题,这两者很容易混淆。
& p+ N# p$ L* C7 y! H像流行的比特币和以太坊这样基于区块链的项目就是这样。“区块链”这个术语通常和像交易、智能合约、加密货币这样的概念紧紧联系在一起。
' s) G: z5 X( ?3 Z! s9 }这就令理解区块链变得不必要得复杂起来,特别是当你想理解源码的时候。下面我将通过 200 行 JS 实现的超级简单的区块链来帮助大家理解它,我给这段代码起名为 NaiveChain。你可以在Github(https://github.com/lhartikk/naivechain)查看更多的技术细节。
9 \9 C4 [5 A) J) V/ q块结构
% Q2 @1 E, N# J第一个逻辑步骤是决定块结构。为了保证事情尽可能的简单,我们只选择最必要的部分:index(下标)、timestamp(时间戳)、data(数据)、hash(哈希值)和 previous hash(前置哈希值)。0 }1 g. p+ ~, [6 N+ M) H
3 p! i- ?  L2 l5 n5 h
这个块中必须能找到前一个块的哈希值,以此来保证整条链的完整性,JavaScript代码如下:% `# S. _& T$ e# k) q
class Block {  m+ `6 b; t0 I
    constructor(index, previousHash, timestamp, data, hash) {
3 Z8 \- P* ~' _        this.index = index;$ ?- H% Z7 v' Z$ g
        this.previousHash = previousHash.toString();
' G, f5 D1 p3 H+ w, L        this.timestamp = timestamp;7 x4 ]4 T; q( U+ \# r' ^
        this.data = data;. A: x1 B5 H7 O! f7 r0 G
        this.hash = hash.toString();
9 s& M, |1 o7 ^' y4 Q9 G  t    }
4 [0 ]- }/ F7 F' `}7 G4 @  Z/ @# i. @' z2 S
块哈希
" M! f5 _7 X4 O' F( e; Z/ [( f为了保存完整的数据,必须哈希区块。SHA-256会对块的内容进行加密,记录这个值应该和“挖矿”毫无关系,因为这里不需要解决工作量证明的问题。JavaScript代码如下:
# e8 }" _7 ]. E* P( p8 V9 evar calculateHash = (index, previousHash, timestamp, data) => {
: b' U4 a; a7 o3 B5 j    return CryptoJS.SHA256(index + previousHash + timestamp + data).toString();" |$ B$ U' D  j7 R/ ~, J! q8 d
};
: i# i6 i$ X% l: J9 F( O% R2 t; h块的生成* @9 \" S- ^& w" X
要生成一个块,必须知道前一个块的哈希值,然后创造其余所需的内容(= index, hash, data and timestamp)。6 e+ u; j+ M7 ^' z  H
块的data部分是由终端用户所提供的。JavaScript代码如下:
6 C) u, B  x4 {* |" u3 qvar generateNextBlock = (blockData) => {$ J' N2 f! V2 g& U
    var previousBlock = getLatestBlock();' |- T' Z- `# \* k: }
    var nextIndex = previousBlock.index + 1;, W) {1 \8 A2 T5 p- N
    var nextTimestamp = new Date().getTime() / 1000;, r1 J! j) g% ?  X
    var nextHash = calculateHash(nextIndex, previousBlock.hash, nextTimestamp, blockData);- R0 J% @! n- J' D9 P9 t
    return new Block(nextIndex, previousBlock.hash, nextTimestamp, blockData, nextHash);
: o5 D; b& T. S5 r4 {};$ n# U, [* ?9 `' }6 r- B, ]. g3 {% O
块的存储
- L9 Y' f0 F( [内存中的Javascript数组被用于存储区块链。区块链的第一个块通常被称为“起源块”,是硬编码的。JavaScript代码如下:
: N5 B- C7 k1 p9 Q2 P6 Rvar getGenesisBlock = () => {, Z4 d$ X8 L3 H
    return new Block(0, "0", 1465154705, "my genesis block!!", "816534932c2b7154836da6afc367695e6337db8a921823784c14378abed4f7d7");# p% @2 \6 W% {, Q' m2 Z. Q
};) n- t* V% ^" ]* g/ @/ T3 I
, g) Y1 x2 [" y3 _( d
var blockchain = [getGenesisBlock()];
$ a6 Z4 m* m1 i9 i: f% X% }确认块的完整性
+ M: u, G. h5 Z6 E& _在任何时候都必须能确认一个区块或者一整条链的区块是否完整。在我们从其他节点接收到新的区块,并需要决定接受或拒绝它们时,这一点尤为重要。JavaScript代码如下:
4 H2 p- H, D& |" ^# wvar isValidNewBlock = (newBlock, previousBlock) => {
6 n+ V9 C- c& x9 n! b% g2 Z    if (previousBlock.index + 1 !== newBlock.index) {
" J: H# ?6 k$ Y* }/ Q& F        console.log('invalid index');
6 x# h0 B: F% u        return false;! h  R% c! l. f) ~
    } else if (previousBlock.hash !== newBlock.previousHash) {
$ R7 R) E: X0 O5 A        console.log('invalid previoushash');% j1 z" B+ ]/ S* }, g
        return false;: O+ D' `$ Y2 V/ j  M5 h( s2 y
    } else if (calculateHashForBlock(newBlock) !== newBlock.hash) {
( d7 D; }& [" G) p+ P' p        console.log('invalid hash: ' + calculateHashForBlock(newBlock) + ' ' + newBlock.hash);* m( q1 i4 p5 z/ i# \) @
        return false;4 s% I! e! w) i: R) L- ]
    }
0 u( D& _& |8 e/ D: z    return true;
1 z2 C! P) R0 Q6 Y$ F};
% Y1 T, h! h5 j9 v6 C  ~6 N5 a选择最长的区块链6 p) `  ?* ~- c7 R

. |8 I8 Y, U* Y/ F% i$ x任何时候在链中都应该只有一组明确的块。万一冲突了(例如:两个结点都生成了72号块时),会选择有最大数目的块的链。JavaScript代码如下:* U& P) T# T6 e' I/ {: P, A
var replaceChain = (newBlocks) => {% J2 r) V7 B, J
    if (isValidChain(newBlocks) && newBlocks.length > blockchain.length) {
- D8 ]& z1 F" J3 [: L0 f5 v+ X# C+ C        console.log('Received blockchain is valid. Replacing current blockchain with received blockchain');
9 M6 V8 \6 S- e' s5 [* y        blockchain = newBlocks;; ]  l% C! R2 g2 `1 k; C/ D
        broadcast(responseLatestMsg());# v2 h7 p% X# K( P$ ]
    } else {( S: D: ~3 i& M4 o$ P2 L% ?& K
        console.log('Received blockchain invalid');
& R8 j- u- L% Z- @( \3 [    }
" ^: d5 _6 b) \, m};
% D- T* h# |% f. l$ L( I. u与其他结点的通信0 X9 r- @1 i1 G7 n' C0 W' ~* u
结点的本质是和其他结点共享和同步区块链,下面的规则能保证网络同步。2 V' W8 I& i5 M! |- d( A

) k. V1 q: d2 L, G+ ?当一个结点生成一个新块时,它会在网络上散布这个块。1 U+ e/ Y+ Q0 [/ u  V6 ^
当一个节点连接新peer时,它会查询最新的block。
4 f) L5 S/ j3 e+ O0 G) }当一个结点遇到一个块,其index大于当前所有块的index时,它会添加这个块到它当前的链中,  或者到整个区块链中查询这个块。4 z  J5 M2 O8 ^
我没有采用自动发现peer的工具。peers的位置(URL)必须是手动添加的。
/ o+ Q% F3 Q9 T- [: H+ |) F节点控制
9 ^* R1 e+ L: o在某种程度上用户必须能够控制节点。这一点通过搭建一个HTTP服务器可以实现。JavaScript代码如下:
, C- e% e3 H+ s9 h: i5 lvar initHttpServer = () => {1 K. K/ y/ {# b
    var app = express();
/ J4 H/ m- ^+ h5 i" U$ k    app.use(bodyParser.json());
( R4 a" L/ n' d1 ^3 N5 A0 _    app.get('/blocks', (req, res) => res.send(JSON.stringify(blockchain)));
. f: x2 F$ {; L" \    app.post('/mineBlock', (req, res) => {
+ S1 r* @2 R8 S, Z3 M        var newBlock = generateNextBlock(req.body.data);# }6 R, Q& o) h
        addBlock(newBlock);
  L* A3 F. g$ i4 }. i7 F        broadcast(responseLatestMsg());
* ]- D9 I, _1 U$ R        console.log('block added: ' + JSON.stringify(newBlock));
/ A: o9 @: l: x- g9 v, o! ]        res.send();5 r2 N( Q- A# ], ^* V  u1 r
    });
0 q5 ^* D5 L! r$ g; x1 F# y    app.get('/peers', (req, res) => {# a3 a0 J! ]5 k$ z" k7 a- C1 k
        res.send(sockets.map(s => s._socket.remoteAddress + ':' + s._socket.remotePort));
: e) ^% s: Q; c& B  K# Z$ A# @" B& K    });
  ^4 l2 n; ?# Y, J    app.post('/addPeer', (req, res) => {
8 V7 m- z/ a& ]) i6 Q+ l+ X9 g+ d        connectToPeers([req.body.peer]);
: j. r; x  m+ ~9 d  o4 B        res.send();3 I, L& R- l* I/ d3 D
    });' R  N# Z' e6 B1 M7 {# b7 n/ h, Z4 J
    app.listen(http_port, () => console.log('Listening http on port: ' + http_port));- @7 U, l0 ]- c5 K% x, Q
};
: V. X( ~, C: b9 G% P用户可以用下面的方法和节点互动:
' g$ O% N+ @0 ?3 L列出所有的块用户提供的内容创建一个新的块列出或者新增peers4 _, S- i" B) T3 `  f
' B  V* s. M8 p  \0 M
下面这个Curl的例子就是最直接的控制节点的方法:, }) y3 b8 \  J5 C' O& N# b5 K
#get all blocks from the node  V8 `8 n$ s0 b1 y5 w
curl http://localhost:3001/blocks7 r7 H  o8 z; l4 S0 U
系统架构7 i5 H2 D* k& Q+ N7 @0 `6 A8 U1 ]& f
需要指出的是,节点实际上展现了两个web服务器:一个(HTTP服务器)是让用户控制节点,另一个(Websocket HTTP服务器)。- k9 d! A  Y/ Q7 b( ~

( }, n9 b- F/ z! ]9 [+ r' h5 ^总结8 v' i$ {; H! i* r) s
创造 NaiveChain 的目的是为了示范和学习,因为它并没有“挖矿”算法(PoS或PoW),不能被用于公用网络,但是它实现了区块链运作的基本特性。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

ranbluer 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    1