Heim > Artikel > Web-Frontend > JavaScript implementiert Blockchain
Viele Freunde haben von Kryptowährungen wie Bitcoin und Ethereum gehört, aber nur wenige Menschen verstehen die Technologie, die sich dahinter verbirgt. Als Nächstes werde ich Ihnen die Verwendung von JavaScript zum Erstellen einer einfachen Blockchain vorstellen intern. Freunde, die Interesse haben, können einen Blick darauf werfen
Fast jeder hat von Kryptowährungen wie Bitcoin und Ethereum gehört, aber nur wenige wissen, wie man sie verbirgt. In diesem Artikel werde ich JavaScript verwenden, um eine einfache Blockchain zu erstellen, um zu zeigen, wie sie intern funktioniert. Ich werde es SavjeeCoin nennen!
Der vollständige Text ist in drei Teile unterteilt:
Teil1: Implementierung einer grundlegenden Blockchain
Teil2: Implementierung von POW
Teil 3: Transaktions- und Mining-Belohnungen
Teil 1: Implementierung einer grundlegenden Blockchain
Blockchain
Blockchain ist eine öffentliche Datenbank, die aus Blöcken besteht, auf die jeder zugreifen kann. Das scheint vielleicht nichts Besonderes zu sein, aber sie haben eine interessante Eigenschaft: Sie sind unveränderlich. Sobald ein Block zur Blockchain hinzugefügt wurde, kann er nicht mehr geändert werden, ohne die verbleibenden Blöcke ungültig zu machen.
Aus diesem Grund basieren Kryptowährungen auf Blockchain. Sie möchten nicht, dass die Leute ihre Angebote ändern, nachdem sie abgeschlossen sind!
Erstellen Sie einen Block
Blockchain besteht aus vielen miteinander verbundenen Blöcken (das klingt nach nichts Falschem ...). Mit Blöcken in der Kette können wir erkennen, ob jemand einen der vorherigen Blöcke manipuliert hat.
Wie stellen wir also die Datenintegrität sicher? Jeder Block enthält einen Hash, der basierend auf seinem Inhalt berechnet wird. Es enthält auch den Hash des vorherigen Blocks.
So sieht eine in JavaScript geschriebene Blockklasse aus:
const SHA256 = require("crypto-js/sha256"); class Block { constructor(index, timestamp, data, previousHash = '') { this.index = index; this.previousHash = previousHash; this.timestamp = timestamp; this.data = data; this.hash = this.calculateHash(); } calculateHash() { return SHA256(this.index + this.previousHash + this.timestamp + JSON.stringify(this.data)).toString(); } }
Da JavaScript sha256 nicht unterstützt, habe ich es verschlüsselt eingeführt. js-Bibliothek. Dann habe ich einen Konstruktor definiert, um die Eigenschaften meines Blocks zu initialisieren. Jeder Block erhält ein Indexattribut, das uns die Position dieses Blocks in der gesamten Kette mitteilt. Wir haben auch einen Zeitstempel und einige Daten generiert, die im Block gespeichert werden müssen. Schließlich gibt es noch den Hash des vorherigen Blocks.
Erstellen Sie eine Kette
Jetzt können wir die Blöcke im Blockchain-Kurs miteinander verknüpfen! Hier ist der in JavaScript implementierte Code:
class Blockchain{ constructor() { this.chain = [this.createGenesisBlock()]; } createGenesisBlock() { return new Block(0, "01/01/2017", "Genesis block", "0"); } getLatestBlock() { return this.chain[this.chain.length - 1]; } addBlock(newBlock) { newBlock.previousHash = this.getLatestBlock().hash; newBlock.hash = newBlock.calculateHash(); this.chain.push(newBlock); } isChainValid() { for (let i = 1; i < this.chain.length; i++){ const currentBlock = this.chain[i]; const previousBlock = this.chain[i - 1]; if (currentBlock.hash !== currentBlock.calculateHash()) { return false; } if (currentBlock.previousHash !== previousBlock.hash) { return false; } } return true; } }
Im Konstruktor initialisiere ich die gesamte Kette, indem ich ein Array erstelle, das den Genesis-Block enthält. Der erste Block ist etwas Besonderes, da er nicht auf den vorherigen Block verweisen kann. Ich habe außerdem die folgenden zwei Methoden hinzugefügt:
getLatestBlock() gibt den neuesten Block auf unserer Blockchain zurück.
addBlock() ist für das Hinzufügen neuer Blöcke zu unserer Kette verantwortlich. Dazu fügen wir den Hash des vorherigen Blocks zu unserem neuen Block hinzu. Auf diese Weise können wir die Integrität der gesamten Kette aufrechterhalten. Denn solange wir den Inhalt des neuesten Blocks ändern, müssen wir seinen Hash neu berechnen. Wenn die Berechnung abgeschlossen ist, schiebe ich den Block in die Kette (ein Array).
Zuletzt erstelle ich ein isChainValid()
, um sicherzustellen, dass niemand die Blockchain manipuliert hat. Es durchläuft alle Blöcke, um zu prüfen, ob der Hash jedes Blocks korrekt ist. Es prüft, ob jeder Block auf den richtigen vorherigen Block verweist, indem es previousHash
vergleicht. Es wird „true“ zurückgegeben, wenn alles in Ordnung ist, andernfalls wird „false“ zurückgegeben.
Blockchain verwenden
Unser Blockchain-Kurs wurde geschrieben und wir können tatsächlich damit beginnen, ihn zu verwenden!
let savjeeCoin = new Blockchain(); savjeeCoin.addBlock(new Block(1, "20/07/2017", { amount: 4 })); savjeeCoin.addBlock(new Block(2, "20/07/2017", { amount: 8 }));
Hier habe ich gerade eine Instanz der Blockchain erstellt und sie SavjeeCoin genannt! Danach habe ich der Kette einige Blöcke hinzugefügt. Der Block kann beliebige Daten enthalten, aber im obigen Code habe ich mich dafür entschieden, ein Objekt mit einer Betragseigenschaft hinzuzufügen.
Probieren Sie es aus!
In der Einleitung habe ich gesagt, dass Blockchain unveränderlich ist. Nach dem Hinzufügen kann der Block nicht mehr geändert werden. Lass es uns versuchen!
// 检查是否有效(将会返回true) console.log('Blockchain valid? ' + savjeeCoin.isChainValid()); // 现在尝试操作变更数据 savjeeCoin.chain[1].data = { amount: 100 }; // 再次检查是否有效 (将会返回false) console.log("Blockchain valid? " + savjeeCoin.isChainValid());
Ich würde die Integrität der gesamten Kette zu Beginn überprüfen, indem ich isChainValid() ausführe. Wir haben einen beliebigen Block bearbeitet, daher wird er „true“ zurückgeben.
Danach habe ich die Daten des ersten (Index 1) Blocks in der Kette geändert. Anschließend überprüfte ich erneut die Integrität der gesamten Kette und stellte fest, dass sie false zurückgab. Unsere gesamte Kette funktioniert nicht mehr.
Fazit
Diese kleine Kastanie ist noch lange nicht fertig. Es wurde kein POW (Proof of Work-Mechanismus) oder P2P-Netzwerk implementiert, um mit anderen Minern zu kommunizieren.
Aber er hat bewiesen, wie Blockchain funktioniert. Viele Leute denken, dass die Prinzipien sehr kompliziert sein werden, aber dieser Artikel beweist, dass die Grundkonzepte der Blockchain sehr einfach zu verstehen und umzusetzen sind.
Teil2: Umsetzung von POW (Proof-of-Work: Arbeitsnachweis)
在part1中我们用JavaScript创建了一个简单的区块链来演示区块链的工作原理。不过这个实现并不完整,很多人发现依旧可以篡改该系统。没错!我们的区块链需要另一种机制来抵御攻击。那么让我们来看看我们该如何做到这一点!
问题
现在我们可以很快的创造区块然后非常迅速的将它们添加进我们的区块链中。不过这导致了三个问题:
第一:人们可以快速创建区块然后在我们的链里塞满垃圾。大量的区块会导致我们区块链过载并让其无法使用。
第二:因为创建一个有效的区块太容易了,人们可以篡改链中的某一个区块,然后重新计算所有区块的hash。即使它们已经篡改了区块,他们仍然可以以有效的区块来作为结束。
第三:你可以通过结合上述两个破绽来有效控制区块链。区块链由p2p网络驱动,其中节点会将区块添加到可用的最长链中。所以你可以篡改区块,然后计算所有其他的区块,最后添加多任意你想要添加的区块。你最后会得到一个最长的链,所有的其它节点都会接受它然后往上添加自己的区块。
显然我们需要一个方案来解决这些问题:POW。
什么是POW
POW是在第一个区块链被创造之前就已经存在的一种机制。这是一项简单的技术,通过一定数量的计算来防止滥用。工作量是防止垃圾填充和篡改的关键。如果它需要大量的算力,那么填充垃圾就不再值得。
比特币通过要求hash以特定0的数目来实现POW。这也被称之为 难度
不过等一下!一个区块的hash怎么可以改变呢?在比特币的场景下,一个区块包含有各种金融交易信息。我们肯定不希望为了获取正确的hash而混淆了那些数据。
为了解决这个问题,区块链添加了一个 nonce 值。Nonce是用来查找一个有效Hash的次数。而且,因为无法预测hash函数的输出,因此在获得满足难度条件的hash之前,只能大量组合尝试。寻找到一个有效的hash(创建一个新的区块)在圈内称之为挖矿。
在比特币的场景下,POW确保每10分钟只能添加一个区块。你可以想象垃圾填充者需要多大的算力来创造一个新区块,他们很难欺骗网络,更不要说篡改整个链。
实现POW
我们该如何实现呢?我们先来修改我们区块类并在其构造函数中添加Nonce变量。我会初始化它并将其值设置为0。
constructor(index, timestamp, data, previousHash = '') { this.index = index; this.previousHash = previousHash; this.timestamp = timestamp; this.data = data; this.hash = this.calculateHash(); this.nonce = 0; }
我们还需要一个新的方法来增加Nonce,直到我们获得一个有效hash。强调一下,这是由难度决定的。所以我们会收到作为参数的难度。
mineBlock(difficulty) { while (this.hash.substring(0, difficulty) !== Array(difficulty + 1).join("0")) { this.nonce++; this.hash = this.calculateHash(); } console.log("BLOCK MINED: " + this.hash); }
最后,我们还需要更改一下 calculateHash()
函数。因为目前他还没有使用Nonce来计算hash。
calculateHash() { return SHA256(this.index + this.previousHash + this.timestamp + JSON.stringify(this.data) + this.nonce ).toString(); }
将它们结合在一起,你会得到如下所示的区块类:
class Block { constructor(index, timestamp, data, previousHash = '') { this.index = index; this.previousHash = previousHash; this.timestamp = timestamp; this.data = data; this.hash = this.calculateHash(); this.nonce = 0; } calculateHash() { return SHA256(this.index + this.previousHash + this.timestamp + JSON.stringify(this.data) + this.nonce).toString(); } mineBlock(difficulty) { while (this.hash.substring(0, difficulty) !== Array(difficulty + 1).join("0")) { this.nonce++; this.hash = this.calculateHash(); } console.log("BLOCK MINED: " + this.hash); } }
修改区块链
现在,我们的区块已经拥有Nonce并且可以被开采了,我们还需要确保我们的区块链支持这种新的行为。让我们先在区块链中添加一个新的属性来跟踪整条链的难度。我会将它设置为2(这意味着区块的hash必须以2个0开头)。
constructor() { this.chain = [this.createGenesisBlock()]; this.difficulty = 2; }
现在剩下要做的就是改变 addBlock() 方法,以便在将其添加到链中之前确保实际挖到该区块。下面我们将难度传给区块。
addBlock(newBlock) { newBlock.previousHash = this.getLatestBlock().hash; newBlock.mineBlock(this.difficulty); this.chain.push(newBlock); }
大功告成!我们的区块链现在拥有了POW来抵御攻击了。
测试
现在让我们来测试一下我们的区块链,看看在POW下添加一个新区块会有什么效果。我将会使用之前的代码。我们将创建一个新的区块链实例然后往里添加2个区块。
let savjeeCoin = new Blockchain(); console.log('Mining block 1'); savjeeCoin.addBlock(new Block(1, "20/07/2017", { amount: 4 })); console.log('Mining block 2'); savjeeCoin.addBlock(new Block(2, "20/07/2017", { amount: 8 }));
如果你运行了上面的代码,你会发现添加新区块依旧非常快。这是因为目前的难度只有2(或者你的电脑性能非常好)。
如果你创建了一个难度为5的区块链实例,你会发现你的电脑会花费大概十秒钟来挖矿。随着难度的提升,你的防御攻击的保护程度越高。
免责声明
就像之前说的:这绝不是一个完整的区块链。它仍然缺少很多功能(像P2P网路)。这只是为了说明区块链的工作原理。
并且:由于单线程的原因,用JavaScript来挖矿并不快。
Part3:交易与挖矿奖励
在前面两部分我们创建了一个简单的区块链,并且加入了POW来抵御攻击。然而我们在途中也偷了懒:我们的区块链只能在一个区块中存储一笔交易,而且矿工没有奖励。现在,让我们解决这个问题!
重构区块类
现在一个区块拥有 index , previousHash , timestamp , data , hash 和 nonce 属性。这个 index 属性并不是很有用,事实上我甚至不知道为什么开始我要将它添加进去。所以我把它移除了,同时将 data 改名为 transactions 来更语义化。
class Block{ constructor(timestamp, transactions, previousHash = '') { this.previousHash = previousHash; this.timestamp = timestamp; this.transactions = transactions; this.hash = this.calculateHash(); this.nonce = 0; } }
当我们改变区块类时,我们也必须更改 calculateHash() 函数。现在它还在使用老旧的 index 和 data 属性。
calculateHash() { return SHA256(this.previousHash + this.timestamp + JSON.stringify(this.transactions) + this.nonce).toString(); }
交易类
在区块内,我们将可以存储多笔交易。因此我们还需要定义一个交易类,一边我们可以锁定交易应当具有的属性:
class Transaction{ constructor(fromAddress, toAddress, amount){ this.fromAddress = fromAddress; this.toAddress = toAddress; this.amount = amount; } }
这个交易例子非常的简单,仅仅包含了发起方( fromAddress )和接受方( toAddress )以及数量。如果有需求,你也可以在里面加入更多字段,不过这个只是为了最小实现。
调整我们的区块链
当前的最大任务:调整我们的区块链来适应这些新变化。我们需要做的第一件事就是存储待处理交易的地方。
正如你所知道的,由于POW,区块链可以稳定的创建区块。在比特币的场景下,难度被设置成大约每10分钟创建一个新区块。但是,是可以在创造两个区块之间提交新的交易。
为了做到这一点,首先需要改变我们区块链的构造函数,以便他可以存储待处理的交易。我们还将创造一个新的属性,用于定义矿工获得多少钱作为奖励:
class Blockchain{ constructor() { this.chain = [this.createGenesisBlock()]; this.difficulty = 5; // 在区块产生之间存储交易的地方 this.pendingTransactions = []; // 挖矿回报 this.miningReward = 100; } }
下一步,我们将调整我们的 addBlock() 方法。不过我的调整是指删掉并重写它!我们将不再允许人们直接为链上添加区块。相反,他们必须将交易添加至下一个区块中。而且我们将 addBlock() 更名为 createTransaction() ,这看起来更语义化:
createTransaction(transaction) { // 这里应该有一些校验! // 推入待处理交易数组 this.pendingTransactions.push(transaction); }
挖矿
人们现在可以将新的交易添加到待处理交易的列表中。但无论如何,我们需要将他们清理掉并移入实际的区块中。为此,我们来创建一个 minePendingTransactions()
方法。这个方法不仅会挖掘所有待交易的新区块,而且还会向采矿者发送奖励。
minePendingTransactions(miningRewardAddress) { // 用所有待交易来创建新的区块并且开挖.. let block = new Block(Date.now(), this.pendingTransactions); block.mineBlock(this.difficulty); // 将新挖的看矿加入到链上 this.chain.push(block); // 重置待处理交易列表并且发送奖励 this.pendingTransactions = [ new Transaction(null, miningRewardAddress, this.miningReward) ]; }
请注意,该方法采用了参数 miningRewardAddress 。如果你开始挖矿,你可以将你的钱包地址传递给此方法。一旦成功挖到矿,系统将创建一个新的交易来给你挖矿奖励(在这个栗子里是100枚币)。
有一点需要注意的是,在这个栗子中,我们将所有待处理交易一并添加到一个区块中。但实际上,由于区块的大小是有限制的,所以这是行不通的。在比特币里,一个区块的大小大概是2Mb。如果有更多的交易能够挤进一个区块,那么矿工可以选择哪些交易达成哪些交易不达成(通常情况下费用更高的交易容易获胜)。
地址的余额
在测试我们的代码钱让我们再做一件事!如果能够检查我们区块链上地址的余额将会更好。
getBalanceOfAddress(address){ let balance = 0; // you start at zero! // 遍历每个区块以及每个区块内的交易 for(const block of this.chain){ for(const trans of block.transactions){ // 如果地址是发起方 -> 减少余额 if(trans.fromAddress === address){ balance -= trans.amount; } // 如果地址是接收方 -> 增加余额 if(trans.toAddress === address){ balance += trans.amount; } } } return balance; }
测试
好吧,我们已经完成并可以最终一切是否可以正常工作!为此,我们创建了一些交易:
let savjeeCoin = new Blockchain(); console.log('Creating some transactions...'); savjeeCoin.createTransaction(new Transaction('address1', 'address2', 100)); savjeeCoin.createTransaction(new Transaction('address2', 'address1', 50));
这些交易目前都处于等待状态,为了让他们得到证实,我们必须开始挖矿:
console.log('Starting the miner...'); savjeeCoin.minePendingTransactions('xaviers-address');
当我们开始挖矿,我们也会传递一个我们想要获得挖矿奖励的地址。在这种情况下,我的地址是 xaviers-address (非常复杂!)。
之后,让我们检查一下 xaviers-address 的账户余额:
console.log('Balance of Xaviers address is', savjeeCoin.getBalanceOfAddress('xaviers-address')); // 输出: 0
我的账户输出竟然是0?!等等,为什么?难道我不应该得到我的挖矿奖励么?那么,如果你仔细观察代码,你会看到系统会创建一个交易,然后将您的挖矿奖励添加为新的待处理交易。这笔交易将会包含在下一个区块中。所以如果我们再次开始挖矿,我们将收到我们的100枚硬币奖励!
console.log('Starting the miner again!'); savjeeCoin.minePendingTransactions("xaviers-address"); console.log('Balance of Xaviers address is', savjeeCoin.getBalanceOfAddress('xaviers-address')); // 输出: 100
局限性与结论
现在我们的区块链已经可以在一个区块上存储多笔交易,并且可以为矿工带来回报。
Allerdings gibt es einige Nachteile: Beim Senden von Währung prüfen wir nicht, ob der Absender über genügend Guthaben verfügt, um die Transaktion tatsächlich durchzuführen. Dies lässt sich jedoch eigentlich leicht beheben. Wir haben auch kein neues Wallet erstellt und die Transaktion nicht signiert (traditionell erfolgt dies mit Public-/Private-Key-Kryptografie).
Ich habe das Obige für Sie zusammengestellt und hoffe, dass es Ihnen in Zukunft hilfreich sein wird.
Verwandte Artikel:
Von vue-cli erstellte Projekte, wie man die mehrseitige Implementierung konfiguriert
web3.js eth.getRawTransactionByHash(txhash)-Methodenschritte hinzugefügt
nodejs einfache Methode für den Zugriff und Betrieb einer MySQL-Datenbank, Beispiel
Das obige ist der detaillierte Inhalt vonJavaScript implementiert Blockchain. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!