JavaScript 区块浏览器详解

在本章中,让我们构建一个区块浏览器,它允许我们与区块链交互。区块浏览器只是一个用户界面,它允许我们探索区块链内部的数据。它允许我们搜索特定区块、特定交易或特定地址,然后以视觉上吸引人的格式显示特定信息

构建区块资源管理器的第一件事是向区块链添加一些新方法和端点,以便搜索数据。然后,让我们向块资源管理器添加一个前端,以便在浏览器中使用它。

在本章中,我们将介绍以下主题:

那么,让我们开始构建块资源管理器

区块浏览器是一个在线平台,允许您在区块链中导航,搜索各种内容,包括地址、区块、交易等。例如,如果您访问https://www.blockchain.com/explorer 您可以看到比特币和以太坊区块链的区块浏览器实用程序,如下所示:

在此块资源管理器内,您可以在整个区块链中搜索特定块、散列或事务,或任何其他所需数据。该实用程序还将结果显示在易于理解的界面上。例如,如果我们在块资源管理器中搜索Block #549897,您将看到该特定块的所有详细信息,如以下屏幕截图所示:

这正是我们将在本章中为区块链构建的内容

为了使块资源管理器正常工作,我们需要查询区块链中的地址、块哈希和事务 ID,以便我们可以搜索特定参数并获得特定数据。因此,我们需要执行的第一步是构建更多的端点。为此,让我们继续执行以下步骤:

  1. 转到dev/networkNode.js文件,在/consensus端点之后,让我们定义块资源管理器的第一个端点/block/:blockHash,如下所示:
app.get('/block/:blockHash', function(req, res) { 

});

一个特定的blockHash将与该端点一起发送,其结果将简单地向我们返回blockHash的输入对应的块。

  1. 我们将构建的下一个端点是/transaction/:transactionId。其定义如下:
app.get('/transaction/:transactionId', function(req, res) {

});

使用这个端点,发送一个transactionId,在响应中,我们应该期望得到这个 ID 对应的正确事务。

  1. 最后,我们要构建的第三个端点是/address/:address,定义如下:
app.get('/address/:address', function(req, res) {

});

有了这个端点,我们将发送一个特定的地址,作为响应,您应该期望每次该特定地址发送或接收比特币响应时,都能获得与该地址对应的所有交易。您还将了解该地址的当前余额,即该地址当前拥有多少比特币。

因此,这是您将在本章中构建的三个端点。对于这些端点中的每一个,我们将在区块链数据结构中构建一个特定的方法,该方法将在区块链中查询正确的数据片段。因此,让我们创建一些方法来查询区块链中的特定块散列、事务和地址。

让我们构建一个名为getBlock的新方法,它将获取给定的blockHash并在整个区块链中搜索与该特定散列相关的块。要构建getBlock方法,请执行以下步骤:

  1. 进入dev/blockchain.js文件,在chainIsValid方法之后,定义这个新方法如下:
Blockchain.prototype.getBlock = function(blockHash) { 

};
  1. 在该方法中,我们希望迭代整个区块链,并搜索具有特定blockHash值的区块。然后,这个方法将把特定的块返回给我们。我们将借助for循环来完成所有这些:
Blockchain.prototype.getBlock = function(blockHash) { 
    this.chain.forEach(block => {

 });
};

在定义for循环时,我们循环遍历区块链中的每个block

  1. 接下来,在循环内部,借助于if语句来说明条件,如下所示:
Blockchain.prototype.getBlock = function(blockHash) { 
    this.chain.forEach(block => {
            if (block.hash === blockHash) 
    });
};
  1. 为了表示找到了我们要搜索的正确块,我们将使用一个标志。让我们定义此标志变量,如以下代码中突出显示的:
Blockchain.prototype.getBlock = function(blockHash) { 
    let correctBlock = null;
    this.chain.forEach(block => {
            if (block.hash === blockHash) 
    });
};
  1. 当我们遍历链中的所有块时,如果我们遇到正确的块,我们将其分配给correctBlock。让我们按如下方式提及此条件:
Blockchain.prototype.getBlock = function(blockHash) { 
  let correctBlock = null;
    this.chain.forEach(block => {
            if (block.hash === blockHash) correctBlock = block;  
    });
};
  1. 最后,在这个方法的末尾,我们希望返回correctBlock,如下所示:
Blockchain.prototype.getBlock = function(blockHash) { 
  let correctBlock = null;
    this.chain.forEach(block => {
            if (block.hash === blockHash) correctBlock = block;  
    });
    return correctBlock
};

我们使用/block/:blockHash端点内部的getBlock方法,通过其blockHash来检索特定的块,接下来我们按照以下步骤来构建端点:

  1. 在这个端点中,我们要做的第一件事是使用与/block/:blockHash请求一起发送的blockHash值。我们可以在req.params对象上访问此blockHash。转到dev/networkNode.js文件,在前面定义的/block/:blockHash端点中,添加以下突出显示的代码:
app.get('/block/:blockHash', function(req, res) { 
        const blockHash = req.params.blockHash;
});

本质上,当我们点击/block/:blockHash端点时,我们正在访问网络中特定节点上存在的块的散列值。我们还使用req.params对象访问散列值,这将允许我们访问/block/:blockHashURL 中前面有冒号的任何值。因此,当用户向该端点发出请求时,他们将在 URL 中发送一个blockHash,然后我们可以在req.params.blockHash的帮助下获取该blockHash。然后我们将把该值保存在blockHash变量中。

  1. 接下来,在端点内部,我们想使用我们在上一节中创建的getBlock方法。我们将向端点添加该方法,如以下代码中突出显示的:
app.get('/block/:blockHash', function(req, res) { 
        const blockHash = req.params.blockHash; const correctBlock = bitcoin.getBlock(blockHash);
});

在代码的这一点上,我们正在寻找的块应该出现在correctBlock变量中。

  1. 最后,返回correctBlock变量作为响应,因此让我们向端点添加以下突出显示的代码:
app.get('/block/:blockHash', function(req, res) { 
        const blockHash = req.params.blockHash;const correctBlock = bitcoin.getBlock(blockHash);
        res.json({
 block: correctBlock
 });
});

这就是我们使用getBlock方法构建/block/:blockHash端点的方式。现在,让我们测试这个端点并验证它是否正常工作

要测试/block/:blockHash端点,请执行以下步骤:

  1. 让我们首先检查区块链中存在多少块。进入浏览器,在地址栏中键入localhost:3001/blockchain,然后按输入。您将看到区块链内部的单个 genesis 区块,如下所示:

  1. 您需要向该链添加更多块。要执行此操作,请转到浏览器中的另一个选项卡,键入localhost:3001/mine,然后按输入。使用相同的过程,让我们再生成一个块。我们现在应该在链中有三个块:一个 genesis 块和我们刚才添加的两个块
  2. 为了测试/block/:blockHash端点,让我们简单地获取其中一个块的哈希值,并使用它来测试端点。让我们复制链中第三个块的哈希值,如以下屏幕截图所示:

  1. 接下来,转到浏览器中的另一个选项卡。在地址栏中键入localhost:3001/block,然后将我们直接复制的哈希值粘贴到此 URL 之后。为了更好地理解,请查看以下屏幕截图:

  1. 现在,我们知道我们使用的散列在链的第三个块中。因此,我们应该期望在运行/block/:blockHash端点后将第三个块返回给我们。现在按回车键,正确的块应该作为输出返回给我们:

从前面的屏幕截图中,我们可以看到返回给我们的是正确的块。返回的块由我们在/block/:blockHash端点中用于搜索块的哈希值组成

以类似的方式,您现在可以尝试使用端点和该特定块的哈希值从链中搜索另一个块

现在,如果我们要发送错误的散列或端点不存在的散列,那么我们应该期望返回 null 作为输出,而不是返回块。让我们通过向/block/:blockHash端点发送错误的散列值来尝试这一点。在浏览器的地址栏中,键入localhost:3001/block,然后向其添加一个伪哈希值,然后按输入。应返回以下输出:

从前面的屏幕截图可以看出,block等于null。这意味着用于搜索块的哈希值在链中不存在。因此,从测试中,我们可以得出结论,/block/:blockHash端点工作正常,符合预期

让我们在区块链数据结构上添加一个名为getTransaction的新方法。这将允许我们通过transactionId获得特定交易。我们将在/transaction/:transactionId端点内部使用此新方法。那么,让我们开始吧!

  1. 进入dev/blockchain.js文件,在getBlock方法之后,定义getTransaction如下:
Blockchain.prototype.getTransaction = function(transactionId) { 

}):

此方法与getBlock方法非常相似。在这里,我们将遍历整个链,并将设置一个标志,该标志等于我们正在寻找的正确事务。

  1. 构建此方法的下一步是迭代整个区块链。为此,请按如下方式使用forEach循环:
Blockchain.prototype.getTransaction = function(transactionId) { 
       this.chain.forEach(block => { 

 });

}):
  1. 因为,在这个方法中,我们寻找事务,我们需要迭代链中每个块上的每个事务。因此,我们需要在前面的for循环中添加另一个for循环:
Blockchain.prototype.getTransaction = function(transactionId) { 
       this.chain.forEach(block => { 
               block.transactions.forEach(transaction => { 

 });
       });

});
  1. 现在我们可以访问区块链上的每一笔交易,我们只需要将每一笔交易的transactionId与我们正在寻找的transactionId进行比较。当两者匹配时,我们就知道找到了正确的交易。让我们在循环中定义此条件,如下所示:
Blockchain.prototype.getTransaction = function(transactionId) { 
       this.chain.forEach(block => { 
               block.transactions.forEach(transaction => { 
                       if (transaction.transactionId === transactionId) {

 }; 
               });
       });

});
  1. 接下来,就像我们在getBlock方法中所做的一样,我们想设置一个标志,表明我们在getTransaction方法中找到了正确的事务。因此,在两个循环的顶部,定义 flag 变量并按如下方式使用它:
Blockchain.prototype.getTransaction = function(transactionId) {
       let correctTransaction = null; 
       this.chain.forEach(block => { 
               block.transactions.forEach(transaction => { 
                       if (transaction.transactionId === transactionId) {
                               correctTransaction = transaction;         

                       }; 
               });
       });

});
  1. 现在,为了使这个方法更有用一点,我们还将发回找到我们正在寻找的事务的块。为此,请定义另一个标志,如下所示:
let correctBlock = null;
  1. 如果我们找到了要查找的交易,请按以下方式设置条件:
Blockchain.prototype.getTransaction = function(transactionId) {
       let correctTransaction = null;
       let correctBlock = null;  
       this.chain.forEach(block => { 
               block.transactions.forEach(transaction => { 
                       if (transaction.transactionId === transactionId) {
                             correctTransaction = transaction;         
                               correctBlock = block; 
                       }; 
               });
       });

});
  1. 最后,要做的最后一件事是将这两个变量作为输出返回。让我们在两个循环之外,按如下方式定义此返回条件:
return {
         transaction: correctTransaction,
         block: correctBlock
};

让我们使用上一节中构建的getTransaction方法来构建/transaction/:transactionId端点。开始吧:

  1. 在该端点内部要做的第一件事是存储作为请求参数发送的事务 ID。让我们将其存储在一个transactionId变量中,如下所示:
app.get('/transaction/:transactionId', function(req, res) {
         const transactionId = req.params.transactionId;
});
  1. 接下来要做的是在端点内部使用getTransaction方法。为此,请在前面的代码中添加以下内容:
app.get('/transaction/:transactionId', function(req, res) {
         const transactionId = req.params.transactionId;
         bitcoin.getTransaction(transactionId);   

});
  1. 通过getTransaction方法,我们得到一个返回给我们的对象,该对象包含我们正在寻找的事务以及该事务所在的块。我们希望将此数据存储在名为transactionData的变量中,如下所示:
app.get('/transaction/:transactionId', function(req, res) {
         const transactionId = req.params.transactionId;
         const trasactionData = bitcoin.getTransaction(transactionId);  

});
  1. 最后,我们希望返回一个简单的响应,其中包含transactionData变量:
app.get('/transaction/:transactionId', function(req, res) {
         const transactionId = req.params.transactionId;
         const trasactionData = bitcoin.getTransaction(transactionId);
         res.json({
    transaction: trasactionData.transaction,
    block: trasactionData.block
         });   

});

这就是我们构建/transaction/:transactionId端点的方式

现在,是测试/transaction/:transactionId端点的时候了,以验证它是否按预期工作。然而,在这样做之前,我们需要向区块链添加一些交易数据和区块。

与上一节类似,首先,让我们向区块链添加一些交易和区块:

  1. 因此,前往邮递员处,点击localhost:3001/transaction/broadcast端点,将事务发送到网络中的所有节点。 
  2. 现在,向网络发送几个示例事务。您可以创建事务,如以下屏幕截图所示:

  1. 添加事务数据后,单击发送按钮将事务发送到网络。同样,您可以添加一个以上的"amount": 200事务并将其发送到网络
  2. 接下来,挖掘一个新区块,以便我们可以将这些交易添加到区块链中。在浏览器中,打开一个选项卡并在地址栏中键入localhost:3001/mine,然后将创建新区块:

  1. 接下来,发送另一个"amount": 300事务,并使用前面提到的流程将其发送到网络。一旦交易被发送,让我们再次挖掘一个区块,将交易添加到区块链:

  1. 现在,再添加两个交易,"amount":值为400500,并将它们发送到网络。最后,再次挖掘一个区块,将我们现在创建的交易添加到区块链中:

现在,如果您转到localhost:3001/blockchain,您将看到我们刚才添加到区块链的所有区块和交易

在向区块链添加交易和区块后,让我们测试/transaction/:transactionId端点:

  1. 转到浏览器并打开另一个选项卡。在地址栏中,键入localhost:3001/transaction/,然后将区块链中存在的任何块中的transactionId值附加到此 URL 的末尾,然后按输入。请查看以下屏幕截图以供参考:

  1. 运行此端点后,应返回以下输出:

在前面的屏幕截图中,您可以看到我们将与端点传递的transactionId相关联的事务作为输出返回。我们还返回了区块,其中包括我们正在寻找的特定transactionId

  1. 现在,用区块链中不存在的transactionId执行另一个示例。要执行此操作,请转到浏览器并在地址栏中键入localhost:3001/transaction/。完成此操作后,向端点添加一个随机哈希值。请看以下屏幕截图:

  1. 运行此端点时,将得到作为输出返回的值 null,如以下屏幕截图所示:

如前一屏幕截图所示,返回的空值向我们表明该transactionId在区块链中不存在。

从测试中,我们可以得出结论,/transaction/:transactionId端点和getTransaction方法正常工作。

我们将在区块链原型上构建一种称为getAddressData的新方法,我们将在/address/:address端点内使用该方法获取我们正在搜索的特定地址的数据:

  1. 让我们在blockchain.js文件中构建这个新方法。在getTransaction方法之后,定义getAddressData方法如下:
Blockchain.prototype.getAddressData = function(address) {

});
  1. 现在,我们在这个方法中要做的第一件事是获取与地址关联的所有事务,并将它们放入单个数组中。现在让我们定义该数组:
Blockchain.prototype.getAddressData = function(address) {
       const addressTransactions = [];
});
  1. 然后,我们希望循环完成区块链内的所有交易。如果这些块中的任何一个具有我们在事务中作为收件人或发件人搜索的地址,那么我们希望将所有这些事务添加到addressTransactions数组中。让我们定义这个条件如下。第一步是在区块链上的所有区块之间循环:
Blockchain.prototype.getAddressData = function(address) {
       const addressTransactions = [];
       this.chain.forEach(block => {

 }); 
});
  1. 现在,为了访问区块链内的交易,我们需要循环每个区块上存在的所有交易。因此,在forEach循环内部,我们必须定义另一个forEach循环,如下所示:
Blockchain.prototype.getAddressData = function(address) {
       const addressTransactions = [];
       this.chain.forEach(block => {
               block.transactions.forEach(transaction => {

 });
       }); 
});
  1. 现在,在我们刚才定义的forEach循环中,我们可以访问区块链上的每一笔交易。我们只想测试每笔交易,看看发件人或收件人地址是否与我们正在搜索的地址匹配:
Blockchain.prototype.getAddressData = function(address) {
       const addressTransactions = [];
       this.chain.forEach(block => {
              block.transactions.forEach(transaction => {
                       if(transaction.sender === address ||
 transaction.recipient === address) {
 addressTransactions.push(transaction);
 }
               });
       }); 
});

在代码的这一点上,我们在区块链内的所有交易中循环。如果我们遇到一个事务,其中发送方地址或接收方地址等于我们要查找的地址,那么我们将该事务推送到addressTransactions数组中。因此,在两个forEach循环完成后,我们将拥有一个数组,该数组包含与我们正在数组中搜索的地址相关联的所有事务。

我们要做的下一件事是在addressTransactions数组中循环,找出我们正在搜索的地址的余额是多少。为了了解余额:

  1. 我们首先定义一个变量balance,如下所示:
let balance = 0;
  1. 接下来,我们要循环遍历addressTransactions数组中的所有事务。我们将在forEach循环的帮助下进行如下操作:
let balance = 0;
addressTransactions.forEach(transaction => { 

});
  1. 在循环内部,借助于ifelse-if语句来说明条件,如下所示:
let balance = 0;
addressTransactions.forEach(transaction => { 
       if (transaction.recipient === address) balance += transaction.amount;
        else if (transaction.sender === address) balance -= transaction.amount; 
}); 
  1. 最后,在forEach循环的末尾,我们希望返回一个对象,该对象的属性为addressTransactions,与我们的addressTransactions数组匹配,并且与addressBalance相同:
let balance = 0;
addressTransactions.forEach(transaction => { 
       if (transaction.recipient === address) balance += transaction.amount;
        else if (transaction.sender === address) balance -= transaction.amount; 
}); 
return {
 addressTransactions: addressTransactions,
 addressBalance: balance
};

这样,我们就完成了getAddressData方法的构建。

现在,让我们构建/address/:address端点,并在该端点内部使用getAddressData方法。/address/:address端点与/block/:blockHash/transaction/:transactionId端点非常相似,因此您不应该觉得它太具有挑战性:

  1. 在端点内部,我们要做的第一件事是将地址存储在变量中:
app.get('/address/:address', function(req, res) {
       const address = req.params.address;
});
  1. 我们要做的下一件事是使用getAddressData方法获取给定地址的所有数据。为此,我们将向端点添加以下突出显示的代码:
app.get('/address/:address', function(req, res) {
       const address = req.params.address;
       bitcoin.getAddressData(address);
});
  1. 通过这个方法,我们得到一个返回给我们的对象,其中包含addressTransactionsaddressBalance。我们希望将此数据存储在变量中,如下所示:
app.get('/address/:address', function(req, res) {
       const address = req.params.address;
       const addressData = bitcoin.getAddressData(address);
});
  1. 然后,最后,我们希望返回包含此数据的响应,如下所示:
app.get('/address/:address', function(req, res) {
       const address = req.params.address;
       const addressData = bitcoin.getAddressData(address);
       res.json({
 addressData: addressData
 }); 

});

这就是我们构建/address/:address端点的方式。现在,让我们测试这个端点,以检查它是否工作正常

为了测试端点,我们需要向区块链添加一些交易数据,让我们按照以下步骤进行操作:

  1. 进入浏览器,探索localhost:3001上的区块链。你会发现这里只有一个街区。因此,让我们向其中添加更多事务数据和块。

  2. 要执行此操作,请转到邮递员并将交易数据发送到localhost:3001/transaction/broadcast。在创建这些事务时,我们希望确保跟踪特定地址,以便在测试/address/:address端点时检查它。为了跟踪这个特定的地址,让我们将其中一个地址的前三个字母改为 JEN。

  3. 让我们创建第一个事务。将"amount":值设置为100,并将JEN添加到本次交易的发送方地址:

  1. 然后,点击发送将交易发送到节点3001。然后,在类似的行中,为amount: 200进行另一个交易,这一次,将JEN添加到收件人的地址,并将发件人的地址保留为随机散列:

  1. 现在,挖掘一个区块,将这些交易添加到区块链中。转到localhost:3001/mine并在链中挖掘一个新块,如下所示:

类似地,您可以通过更改金额值并交换发件人和收件人的地址(地址中有JEN)来进行更多交易。一旦创建了一些交易,挖掘一个区块,将这些新交易添加到区块链中。然后,再次创建新的交易,并通过交换发送者和接收者的地址为它们提供不同的金额。再次挖掘一个新区块,将交易添加到区块链中

然后,您可以通过进入localhost:3001/blockchain浏览整个区块链,以及我们添加到其中的新交易和区块。您将看到区块链中的一系列区块和交易

现在,为了测试/address/:address端点,让我们按照以下步骤进行操作:

  1. 转到浏览器,在新选项卡中点击localhost:3001/address/端点。
  2. 然后,从我们刚刚添加到区块链的交易中复制一个地址,并将其粘贴到端点。请查看以下屏幕截图以供参考:

  1. 现在,当我们运行这个端点时,我们应该看到与该特定地址关联的所有交易,以及该特定地址的比特币余额。请看以下屏幕截图:

在前面的屏幕截图中,我们得到返回的addressData属性,它由addressTransactions数组和addressBalance属性组成。addressTransactions数组包含与端点中提到的地址相关联的所有事务。此外,addressBalance属性包括我们在端点中提到的地址的比特币余额:

  1. 接下来,您可以尝试通过复制挖掘奖励交易的收件人地址并将其粘贴到/address/:address端点来检查节点地址的余额,就像我们在前面的示例中所做的那样。
  2. 运行此端点后,您将看到挖掘奖励事务的平衡。尝试实现许多其他类似的示例,以清楚地了解/address/:address端点是如何工作的。
  3. 您可以尝试实现的另一个示例是传递区块链中不存在的地址。您将得到以下返回的响应:

从前面的屏幕截图中,我们可以看到addressTransactions数组是空的,因为没有与我们作为输入的不存在的地址相关联的事务。此外,不存在的地址的addressBalance值为0。因此,我们可以从测试中得出结论,/address/:address端点正常工作

让我们了解如何设置块资源管理器前端。区块资源管理器将是一个用户界面,我们可以通过它从浏览器与区块链进行交互。为了构建这个用户界面并使其功能化,我们需要使用 HTML、CSS 和 JavaScript。

现在,您可以通过以下链接找到一个完整的预构建前端,而不是自己构建所有前端:https://github.com/PacktPublishing/Learn-Blockchain-Programming-with-JavaScript/blob/master/dev/block-explorer/index.html 。在本节中,我们没有构建整个前端,因为这不是本书的重点。

要构建前端,只需从提供的链接复制文件并将其添加到项目的文件结构中。现在,转到dev文件夹并在其中创建一个名为block-explorer的新文件夹。在这个block-explorer文件夹中,创建一个名为index.html的文件,您需要将提供的前端代码粘贴到其中,然后保存该文件。在下一节中,您将快速了解此前端代码的组成以及代码的功能

让我们构建一个端点,它将为我们检索block-explorer文件:

  1. 转到dev/networkNode.js文件,在这里,创建一个新的端点,将此文件发送给我们。按如下方式定义端点:
app.get('/block-explorer', function(req, res) {

});
  1. 现在,在这个端点内部,我们要做的就是将index.html文件发回给调用这个端点的人:
app.get('/block-explorer', function(req, res) {
    res.sendFile('./block-explorer/index.html', { root: __dirname });
});

在前面的章节中,您一定注意到我们通常使用res.json,这是发送 JSON 数据的一种方式。但是,在这个端点中,我们希望发送整个文件,因此我们将使用res.sendFile方法。请注意,在前面的代码中,我们使用了{ root: __dirname }。这个代码术语表示我们应该查看项目存储的目录,并在其中搜索具有/block-explorer/index.html路径的文件。这就是为什么我们将此选项作为第二个参数添加到端点,这就是我们如何构建端点以发送index.html文件的原因

  1. 接下来,保存networkNode.js文件,并通过在浏览器中点击localhost:3001/block-explorer来验证该端点是否工作。然后将显示块资源管理器前端,如下所示:

您在这个前端看到的所有内容都包含在我们刚刚创建的index.html文件中。

在本节中,我们将简单地浏览在上一节中创建的index.html文件。我们将这样做是为了更好地了解正在发生的事情。那么,让我们开始吧。

index.html文件中,我们有所有的 HTML 和 JavaScript 代码为块资源管理器提供必要的功能。这段代码还允许我们使用 API,最后,我们只使用了一些 CSS 和样式,使浏览器中的一切看起来都很好。

代码首先导入两个库,例如angular.js,以使用 API,以及 jQuery、Bootstrap 和一些引导样式,以使一切功能化和美观化:

<head>
  <title>Block Explorer</title>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
  <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
  <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
</head>

接下来,我们将看到 HTML 模型的主体,它由块资源管理器标题组成:

<body ng-app="BlockExplorer">
  <div class="container" ng-controller="MainController">
    <div class="row">
      <div class="col-md-8 offset-md-2">
        <h1 id="page-title">Block Explorer</h1>
      </div>
    </div

然后,我们有一个文本输入表单:

<div class="row">
      <div class="col-md-6 offset-md-3">
        <form ng-submit="search(searchValue)">
          <div class="form-group">
            <input type="text" class="form-control" ng-model="searchValue">
          </div>

接下来,我们有一个select输入,其中有三个选项:Block HashTransaction IDAddress

<div class="form-group">
        <select class="form-control" ng-model="searchType">
                <option value="block">Block Hash</option>
                <option value="transaction">Transaction ID</option>
                <option value="address">Address</option>
        </select>
</div>

要使用此页面,让我们在文本字段中输入块哈希、事务 ID 或地址,然后从下拉菜单中选择要查找的内容,如以下屏幕截图所示:

最后,在 HTML 代码中,我们只有一些表,一旦我们从区块链中获得了一些数据,这些表将为我们显示所有数据。

此外,我们的index.html文件中还有一些 JavaScript 代码。在这段 JavaScript 代码中,我们使用 Angular 调用 API:

 window.app = angular.module('BlockExplorer', []);
 app.controller('MainController', function($scope, $http) {
          $scope.block = null;
          $scope.transaction = null;
          $scope.addressData = null;
          $scope.initialSearchMade = false;

然后我们有一个方法,每当我们选择块散列选项时,我们都会点击/block/:blockHash端点:

$scope.fetchBlock = function(blockHash) {
        $http.get(`/block/${blockHash}`)
        .then(response => {
          $scope.block = response.data.block;
          $scope.transaction = null;
          $scope.addressData = null;
        });
      };

同样,我们有/transaction/:transactionId端点的方法:

$scope.fetchTransaction = function(transactionId) {
        $http.get(`/transaction/${transactionId}`)
        .then(response => {
          $scope.transaction = response.data.transaction;
          $scope.block = null;
          $scope.addressData = null;
        }); 
      };

我们还有/address/:address终点的方法:

$scope.fetchAddressData = function(address) {
        $http.get(`/address/${address}`)
        .then(response => {
          $scope.addressData = response.data.addressData;
          if (!$scope.addressData.addressTransactions.length) $scope
            .addressData = null;
          $scope.block = null;
          $scope.transaction = null;
        }); 
      };

在本 JavaScript 的其余部分中,我们只是增加了一点功能,然后在代码的末尾添加了 CSS 样式。因此,这段代码包含在index.html文件中。如果您想更深入地了解这段代码以获得更清晰的理解,请随意这样做。您也可以按自己的意愿对其进行自定义

然后按 Search,如果区块链中存在指定的数据,将显示一个表,显示所有数据。如果数据在我们的区块链上不存在,您将得到未找到数据的结果。这就是块资源管理器前端的工作方式。

在这一点上,我们构建了一个完整的块资源管理器前端,我们有块资源管理器的后端——我们刚刚创建的三个端点,用于搜索整个区块链

在下一节中,我们将测试块资源管理器,以确保其工作正常。

在本节中,我们将测试块资源管理器,以确保其正常工作,并确保我们在上一章中创建的所有端点和方法也正常工作。如果区块资源管理器工作正常,那么我们已经知道整个区块链也在正常工作,并且在分布式区块链网络上运行,因此当我们进入本章的最后一节时,一切都很顺利。因此,这是我们将要进行的最后一次测试。现在让我们按照以下步骤测试块资源管理器:

  1. 为了测试块资源管理器,我们应该确保所有五个节点都在运行。
  2. 下一步,转到浏览器并通过转到localhost:3003/block-explorer打开块资源管理器。实际上,您可以转到托管在网络中任何节点上的块资源管理器,因为区块链托管在整个网络上。
  3. 现在,为了测试块资源管理器,我们需要向区块链添加一些数据。为了向区块链添加数据,我们将创建大量交易,并创建一些与前面章节类似的新区块。您可以参考前面的章节,快速回顾如何向区块链添加交易和区块。
  4. 添加数据后,我们现在可以测试块资源管理器。让我们首先通过搜索块散列来获取块。让我们选择块散列选项:

  1. 然后,从区块链中复制任意块的哈希值,并将其粘贴到块资源管理器中:

  1. 现在,点击搜索按钮。您应该会看到与以下屏幕截图类似的输出:

这就是块资源管理器的基本工作方式。我们输入一个散列或一段我们正在寻找的数据,作为回报,我们得到这段数据作为输出。从前面的屏幕截图中,我们可以看到,我们返回了块,块的索引为4,用于输入到块资源管理器的哈希值。我们还得到了与该街区有关的所有细节。此外,正如您可能在本次搜索中看到的,我们正在到达/block/:blockHash端点

  1. 接下来,通过输入transactionId来搜索交易。转到块资源管理器并选择事务 ID 选项。然后,转到区块链,从任何块复制一个transactionId值,并将其输入到块资源管理器:

  1. 然后单击搜索按钮。您将看到以下类似的输出:

从前面的屏幕截图中,我们可以看到,我们获得了与输入到块资源管理器的transactionId相关的所有事务详细信息。我们还观察了该特定transactionId的 400 比特币余额

  1. 最后,测试地址端点。要执行此操作,请从块资源管理器中选择“地址”选项,然后从任何块中输入发件人或收件人的地址。然后单击搜索按钮。您应该在屏幕上看到以下输出:

从前面的屏幕截图中,我们可以看到该地址的余额为 749.35 比特币,我们可以看到与我们输入的地址相关的所有交易

现在,对于这些搜索中的任何一个,如果我们输入一段不存在的数据,我们将返回如下结果:

这证明了块资源管理器可以正常工作。

在本章中,我们构建了一个令人惊叹的用户界面,以探索到目前为止我们在本书中构建的区块链。我们首先定义查询所需数据所需的端点。然后我们构建了getBlockgetTransactiongetAddressData等方法来帮助端点查询数据。此外,我们还开发了/block/:blockHash/transaction/:transactionId/address/:address终点。完成此操作后,我们将块资源管理器的前端代码添加到区块链目录中,然后测试块资源管理器和我们开发的所有端点

有了这一章,我们就到了这本书的结尾。至此,我们已经构建了自己的区块链,并为其添加了所有必要的功能。除此之外,我们还建立了我们的分布式网络和一个探索区块链的接口

下一章将快速总结我们在本书中所学到的知识。然后,我们将探索利用我们开发的区块链还能做些什么。

教程来源于Github,感谢apachecn大佬的无私奉献,致敬!

技术教程推荐

赵成的运维体系管理课 -〔赵成〕

邱岳的产品实战 -〔邱岳〕

技术管理实战36讲 -〔刘建国〕

白话法律42讲 -〔周甲徳〕

Android开发高手课 -〔张绍文〕

从0打造音视频直播系统 -〔李超〕

SRE实战手册 -〔赵成〕

小马哥讲Spring AOP编程思想 -〔小马哥〕

AI大模型系统实战 -〔Tyler〕