JavaScript はブロックチェーンを実装します
多くの友人はビットコインやイーサリアムなどの暗号通貨について聞いたことがあると思いますが、その背後に隠されたテクノロジーを理解している人はほんのわずかです。 次に、この記事を通じて、JavaScript を使用してそれらをデモンストレーションする簡単なブロックチェーンを作成する方法を紹介します。社内で働いている方、興味のある友人、見てみましょう
ほぼ誰もがビットコインやイーサリアムのような暗号通貨について聞いたことがあるでしょうが、その背後に隠されたテクノロジーを理解している人はごくわずかです。この記事では、JavaScript を使用して簡単なブロックチェーンを作成し、ブロックチェーンが内部でどのように機能するかを示します。それをSavjeeCoinと呼ぶことにします!
全文は 3 つのパートに分かれています:
part1: 基本的なブロックチェーンの実装
part2: POW の実装
part3: トランザクションとマイニング報酬
パート 1: の実装基本的なブロックチェーン
ブロックチェーン
ブロックチェーンは、誰でもアクセスできるブロックで構成される公開データベースです。これは特別なことのように思えないかもしれませんが、それらには不変であるという興味深い特性があります。ブロックがブロックチェーンに追加されると、残りのブロックを無効にすることなく変更することはできません。
これが、暗号通貨がブロックチェーンに基づいている理由です。取引終了後に取引を変更されるのは望ましくありません。
ブロックを作成する
ブロックチェーンは、相互にリンクされた多数のブロックで構成されています(これは大丈夫そうです...)。チェーン上のブロックには、誰かが以前のブロックを操作したかどうかを検出できる方法があります。
では、データの整合性を確保するにはどうすればよいでしょうか?各ブロックには、その内容に基づいて計算されたハッシュが含まれています。前のブロックのハッシュも含まれます。
JavaScriptで書くブロッククラスは大まかにこんな感じです
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(); } }
JavaScriptはsha256をサポートしていないため、crypto-jsライブラリを導入しました。次に、ブロックのプロパティを初期化するコンストラクターを定義しました。各ブロックには、チェーン全体におけるこのブロックの位置を示すインデックス属性が与えられます。また、タイムスタンプと、ブロックに保存する必要があるいくつかのデータも生成しました。最後に、前のブロックのハッシュがあります。
チェーンを作成する
これで、Blockchain クラスでブロックをリンクできるようになりました。 JavaScript で実装されたコードは次のとおりです。
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; } }
コンストラクターで、genesis ブロックを含む配列を作成してチェーン全体を初期化します。最初のブロックは前のブロックを指すことができないため、特別です。次の 2 つのメソッドも追加しました:
getlatestBlock() はブロックチェーン上の最新のブロックを返します。
addBlock() は、チェーンに新しいブロックを追加する役割を果たします。これを行うには、前のブロックのハッシュを新しいブロックに追加します。このようにして、チェーン全体の整合性を維持できます。最新のブロックの内容を変更する限り、そのハッシュを再計算する必要があるからです。計算が完了したら、ブロックをチェーン (配列) にプッシュします。
最後に、各ブロックが前の正しいブロックを指しているかどうかを確認するための isChainValid()
来确保没有人篡改过区块链。它会遍历所有的区块来检查每个区块的hash是否正确。它会通过比较 previousHash
を作成します。すべてがOKな場合は true を返し、それ以外の場合は false を返します。
ブロックチェーンの使用
ブロックチェーン クラスが作成されたので、実際に使用を開始できます。
let savjeeCoin = new Blockchain(); savjeeCoin.addBlock(new Block(1, "20/07/2017", { amount: 4 })); savjeeCoin.addBlock(new Block(2, "20/07/2017", { amount: 8 }));
ここでは、ブロックチェーンのインスタンスを作成し、SavjeeCoin という名前を付けました。その後、チェーンにいくつかのブロックを追加しました。ブロックには任意のデータを含めることができますが、上記のコードでは、amount プロパティを持つオブジェクトを追加することにしました。
試してみてください!
はじめに、ブロックチェーンは不変であると言いました。追加したブロックは変更できません。試してみよう!
// 检查是否有效(将会返回true) console.log('Blockchain valid? ' + savjeeCoin.isChainValid()); // 现在尝试操作变更数据 savjeeCoin.chain[1].data = { amount: 100 }; // 再次检查是否有效 (将会返回false) console.log("Blockchain valid? " + savjeeCoin.isChainValid());
最初に isChainValid() を実行して、チェーン全体の整合性を検証します。任意のブロックを操作したので、true が返されます。
その後、チェーンの最初(インデックス1)のブロックのデータを変更しました。その後、チェーン全体の整合性を再度チェックしたところ、 false が返されたことがわかりました。私たちのチェーン全体がもう機能しません。
結論
この小さな栗は完成には程遠いです。他のマイナーと通信するための POW (プルーフ・オブ・ワーク・メカニズム) や P2P ネットワークは実装されていません。
しかし、彼はブロックチェーンがどのように機能するかを証明しました。多くの人は原理が非常に複雑だと考えていますが、この記事はブロックチェーンの基本概念が非常に理解しやすく、実装しやすいことを証明しています。
Part2: POW(proof-of-work: 作業証明)の導入
在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
局限性与结论
现在我们的区块链已经可以在一个区块上存储多笔交易,并且可以为矿工带来回报。
ただし、まだいくつかの欠点があります。通貨を送金する際、送金元が実際に取引を行うのに十分な残高があるかどうかを確認しません。ただし、これは実際には簡単に修正できます。また、新しいウォレットを作成したり、トランザクションに署名したりしませんでした (従来は公開/秘密キー暗号化を使用して行われていました)。
上記は私があなたのためにまとめたものです。
関連記事:
vue-cli作成プロジェクト、マルチページ実装メソッドの設定
web3.jsにeth.getRawTransactionByHash(txhash)メソッドの手順を追加
nodejs mysqlデータベースへの簡単なアクセスと操作メソッドの例
以上がJavaScript はブロックチェーンを実装しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









Huawei 携帯電話にデュアル WeChat ログインを実装するにはどうすればよいですか?ソーシャルメディアの台頭により、WeChatは人々の日常生活に欠かせないコミュニケーションツールの1つになりました。ただし、多くの人は、同じ携帯電話で同時に複数の WeChat アカウントにログインするという問題に遭遇する可能性があります。 Huawei 社の携帯電話ユーザーにとって、WeChat の二重ログインを実現することは難しくありませんが、この記事では Huawei 社の携帯電話で WeChat の二重ログインを実現する方法を紹介します。まず第一に、ファーウェイの携帯電話に付属するEMUIシステムは、デュアルアプリケーションを開くという非常に便利な機能を提供します。アプリケーションのデュアルオープン機能により、ユーザーは同時に

顔の検出および認識テクノロジーは、すでに比較的成熟しており、広く使用されているテクノロジーです。現在、最も広く使用されているインターネット アプリケーション言語は JS ですが、Web フロントエンドでの顔検出と認識の実装には、バックエンドの顔認識と比較して利点と欠点があります。利点としては、ネットワーク インタラクションの削減とリアルタイム認識により、ユーザーの待ち時間が大幅に短縮され、ユーザー エクスペリエンスが向上することが挙げられます。欠点としては、モデル サイズによって制限されるため、精度も制限されることが挙げられます。 js を使用して Web 上に顔検出を実装するにはどうすればよいですか? Web 上で顔認識を実装するには、JavaScript、HTML、CSS、WebRTC など、関連するプログラミング言語とテクノロジに精通している必要があります。同時に、関連するコンピューター ビジョンと人工知能テクノロジーを習得する必要もあります。 Web 側の設計により、次の点に注意してください。

プログラミング言語 PHP は、さまざまなプログラミング ロジックやアルゴリズムをサポートできる、Web 開発用の強力なツールです。その中でも、フィボナッチ数列の実装は、一般的で古典的なプログラミングの問題です。この記事では、PHP プログラミング言語を使用してフィボナッチ数列を実装する方法を、具体的なコード例を添付して紹介します。フィボナッチ数列は、次のように定義される数学的数列です。数列の最初と 2 番目の要素は 1 で、3 番目の要素以降、各要素の値は前の 2 つの要素の合計に等しくなります。シーケンスの最初のいくつかの要素

Huawei 携帯電話に WeChat クローン機能を実装する方法 ソーシャル ソフトウェアの人気と人々のプライバシーとセキュリティの重視に伴い、WeChat クローン機能は徐々に人々の注目を集めるようになりました。 WeChat クローン機能を使用すると、ユーザーは同じ携帯電話で複数の WeChat アカウントに同時にログインできるため、管理と使用が容易になります。 Huawei携帯電話にWeChatクローン機能を実装するのは難しくなく、次の手順に従うだけです。ステップ 1: 携帯電話システムのバージョンと WeChat のバージョンが要件を満たしていることを確認する まず、Huawei 携帯電話システムのバージョンと WeChat アプリが最新バージョンに更新されていることを確認します。

今日のソフトウェア開発分野では、効率的で簡潔かつ同時実行性の高いプログラミング言語として、Golang (Go 言語) が開発者にますます好まれています。豊富な標準ライブラリと効率的な同時実行機能により、ゲーム開発の分野で注目を集めています。この記事では、ゲーム開発に Golang を使用する方法を検討し、具体的なコード例を通じてその強力な可能性を示します。 1. ゲーム開発における Golang の利点 Golang は静的型付け言語として、大規模なゲーム システムの構築に使用されます。

Golang で正確な除算演算を実装することは、特に財務計算を含むシナリオや高精度の計算が必要なその他のシナリオでよくあるニーズです。 Golang の組み込みの除算演算子「/」は浮動小数点数に対して計算されるため、精度が失われる場合があります。この問題を解決するには、サードパーティのライブラリまたはカスタム関数を使用して、正確な除算演算を実装します。一般的なアプローチは、math/big パッケージの Rat タイプを使用することです。これは分数の表現を提供し、正確な除算演算を実装するために使用できます。

PHP ゲーム要件実装ガイド インターネットの普及と発展に伴い、Web ゲーム市場の人気はますます高まっています。多くの開発者は、PHP 言語を使用して独自の Web ゲームを開発することを望んでおり、ゲーム要件の実装は重要なステップです。この記事では、PHP 言語を使用して一般的なゲーム要件を実装する方法を紹介し、具体的なコード例を示します。 1. ゲームキャラクターの作成 Web ゲームにおいて、ゲームキャラクターは非常に重要な要素です。ゲームキャラクターの名前、レベル、経験値などの属性を定義し、これらを操作するメソッドを提供する必要があります。

js と vue の関係: 1. Web 開発の基礎としての JS、2. フロントエンド フレームワークとしての Vue.js の台頭、3. JS と Vue の補完関係、4. JS と Vue の実用化ビュー。
