您现在的位置是:首页 >技术杂谈 >JS 实现区块链添加可视化功能网站首页技术杂谈

JS 实现区块链添加可视化功能

GoldenaArcher 2024-06-14 17:18:54
简介JS 实现区块链添加可视化功能

JS 实现区块链添加可视化功能

学习的最后一部分了,完整的资源在这里:https://download.csdn.net/download/weixin_42938619/87765530,有需求的可以自取。

最后一部分是增加一些可视化的功能,完整实现后如下:

在这里插入图片描述

在这里插入图片描述

HTML 部分是用 Angular 写的,Angular 的学习不在讨论的范围内,所以这里会将剩下的一些功能补全就是了。

添加查找对应 block

这里主要接收一个 blockId,随后通过 find 功能去查找对应的 block,找到返回对应的 Block,否则返回 null:

getBlock = (blockHash: string): Block | null => {
  return this.chain.find((block) => block.hash === blockHash) || null;
};

API 部分为:

app.get('/block/:blockHash', (req, res) => {
  const { blockHash } = req.params;
  const block = bitcoin.getBlock(blockHash);
  res.json({ block });
});

添加查找对应 transaction

这里需要做两重迭代,同时会返回对应的 transaction 和对应的 block:

getTransaction = (
  transactionId: string
): { transaction: Transaction | null; block: Block | null } => {
  for (const block of this.chain) {
    const transaction = block.transactions.find(
      (transaction) => transaction.transactionId === transactionId
    );

    if (transaction) return { transaction, block };
  }
  return { transaction: null, block: null };
};

api 部分:

app.get('/transaction/:transactionId', (req, res) => {
  const { transactionId } = req.params;
  const transactionAndBlock = bitcoin.getTransaction(transactionId);
  res.json(transactionAndBlock);
});

添加查找对应 address

同样进行两重迭代去查找对应地址的所有交易,为了可读性没有做其他的优化,毕竟从大 O 来说都是 O ( m n ) O(mn) O(mn)

getAddressData = (
  address: string
): { addressTransactions: Transaction[]; addressBalance: number } => {
  const addressTransactions: Transaction[] = [];
  for (const block of this.chain) {
    for (const transaction of block.transactions) {
      if (transaction.sender === address || transaction.recipient === address) {
        addressTransactions.push(transaction);
      }
    }
  }

  const addressBalance = addressTransactions.reduce(
    (accum: number, transaction: Transaction) => {
      if (transaction.recipient === address) accum += transaction.amount;
      else if (transaction.sender === address) accum -= transaction.amount;

      return accum;
    },
    0
  );

  return { addressTransactions, addressBalance };
};

api 部分:

app.get('/address/:address', (req, res) => {
  const { address } = req.params;
  const addressData = bitcoin.getAddressData(address);
  res.json({ addressData });
});

可视化部分

这里主要就是把 html 文件加进来,api 的加载部分如下:

app.get('/block-explorer', (req, res) => {
  res.sendFile('./block-explorer/index.html', {
    root: __dirname,
  });
});

html 部分如下:

<!DOCTYPE html>
<html>
  <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>

  <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>
            <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>
            <button
              type="submit"
              class="btn btn-primary margin-auto btn-search"
            >
              Search
            </button>
            <p
              ng-if="!block && !transaction && !addressData && initialSearchMade"
              class="no-data-text"
            >
              No data found for search.
            </p>
          </form>
        </div>
      </div>
      <div class="row">
        <div class="col-md-10 offset-md-1">
          <!-- 'display block' table -->
          <h3 class="table-title" ng-if="block">Block</h3>
          <table class="table table-striped" ng-if="block">
            <tbody>
              <tr>
                <td class="bold">Block Hash</td>
                <td>{{ block.hash }}</td>
              </tr>
              <tr>
                <td class="bold">Index</td>
                <td>{{ block.index }}</td>
              </tr>
              <tr>
                <td class="bold">Time Stamp</td>
                <td>{{ block.timestamp }}</td>
              </tr>
              <tr>
                <td class="bold">Nonce</td>
                <td>{{ block.nonce }}</td>
              </tr>
              <tr>
                <td class="bold">Previous Hash</td>
                <td>{{ block.previousBlockHash }}</td>
              </tr>
              <tr>
                <td class="bold">Number Transactions</td>
                <td>{{ block.transactions.length }}</td>
              </tr>
            </tbody>
          </table>
          <!-- end 'display block' table -->

          <!-- 'display transaction' table -->
          <h3 class="table-title" ng-if="transaction">Transaction</h3>
          <table class="table table-striped" ng-if="transaction">
            <tbody>
              <tr>
                <td class="bold">Sender</td>
                <td>{{ transaction.sender }}</td>
              </tr>
              <tr>
                <td class="bold">Recipient</td>
                <td>{{ transaction.recipient }}</td>
              </tr>
              <tr>
                <td class="bold">Amount</td>
                <td>{{ transaction.amount }}</td>
              </tr>
            </tbody>
          </table>
          <!-- end 'display transaction' table -->

          <!-- 'display address' table -->
          <h3 class="table-title" ng-if="addressData">Address</h3>
          <p id="balance-text" ng-if="addressData">
            (Balance: {{ addressData.addressBalance }})
          </p>
          <table class="table table-striped" ng-if="addressData">
            <thead>
              <tr>
                <th scope="col">Sender</th>
                <th scope="col">Recipient</th>
                <th scope="col">Amount</th>
              </tr>
            </thead>
            <tbody>
              <tr ng-repeat="transaction in addressData.addressTransactions">
                <td>{{ transaction.sender }}</td>
                <td>{{ transaction.recipient }}</td>
                <td>{{ transaction.amount }}</td>
              </tr>
            </tbody>
          </table>
          <!-- end 'display address' table -->
        </div>
      </div>
    </div>

    <script>
      window.app = angular.module('BlockExplorer', []);

      app.controller('MainController', function ($scope, $http) {
        $scope.block = null;
        $scope.transaction = null;
        $scope.addressData = null;
        $scope.initialSearchMade = false;

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

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

        $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;
          });
        };

        $scope.search = function (searchValue) {
          $scope.initialSearchMade = true;
          if ($scope.searchType === 'block') {
            $scope.fetchBlock(searchValue);
          } else if ($scope.searchType === 'transaction') {
            $scope.fetchTransaction(searchValue);
          } else if ($scope.searchType === 'address') {
            $scope.fetchAddressData(searchValue);
          }
        };
      });
    </script>

    <style type="text/css">
      html,
      body {
        min-height: 100vh;
        background-color: #e8e8e8;
      }
      .container {
        padding-top: 50px;
        padding-bottom: 50px;
      }
      #page-title {
        text-align: center;
        margin-bottom: 40px;
      }
      .table-title {
        margin-bottom: 20px;
        text-align: center;
      }
      .table {
        background-color: #ffffff;
        box-shadow: 2px 2px 15px -3px rgba(0, 0, 0, 0.75);
      }
      #balance-text {
        text-align: center;
        margin-top: -20px;
        margin-bottom: 30px;
      }
      .margin-auto {
        margin: auto;
        display: block;
      }
      .btn-search {
        margin-bottom: 50px;
      }
      .bold {
        font-weight: 700;
      }
      .no-data-text {
        color: red;
        text-align: center;
      }
    </style>
  </body>
</html>
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。