参考1:https://qiita.com/YuasaJunki/items/c18e06dc9a5970813c38
参考2:https://note.com/strictlyes/n/n40e5884229a5

ブロックチェーンとは

仮想通貨やNFTの基盤技術で、分散型ネットワーク下で暗号化技術を組み合わせてセキュアにデータを記録するためのデータ構造である。
取引の記録をブロックという単位にまとめ、チェーンのように時系列順に繋いでデータを蓄積する仕組みであることからブロックチェーンと呼ばれている。
チェーン状にすることで、途中のデータだけを書き換えることができないようにしている。

一つのブロックには「一定期間ごとの取引データ」「前のブロックのハッシュ値」「ナンス値」が含まれている。

取引データには送金額などの取引情報が含まれる。一定期間はビットコインの場合は10分間に一度の取引データがブロックに格納される。
ハッシュ値はGitのコミットのハッシュ値と同じような意味合いポインタである(Gitはブランチによってツリー状になるが、ブロックチェーンはすべて一つのチェーンになっている)。
ナンス(nonce – number used once)は一度だけ使われる32ビットの値であるが何でもいいわけではなく、ハッシュ関数を使用してある値よりも小さい数値になるナンスにしなければならない。
ハッシュ関数のため、狙って数値を調整できるものではないため、繰り返し地道に計算を続ける必要があり、条件を満たすハッシュを見つけられればブロックはチェーンにつながれる(あるいはビットコインが発行される)。
この作業のことを発掘あるいはマイニングと呼び、マイニングを行うことで、ビットコインを報酬としてもらうことができる。
※もし改竄しようとした場合は、そのハッシュ値を見つけるために、前のすべてのブロックのハッシュ値も変更する必要があるため、極めて困難となる

またビットコインでは最も長いブロックチェーンを信用することになっているため、偽のブロックチェーンを作る攻撃をする場合は、「51%攻撃」という残りの採掘者すべてを上回る計算能力を用意する必要があるが、それだけの計算能力があれば普通に発掘をしても利益が出るため、攻撃のインセンティブを下げることにもつながる。

ブロックチェーンの特徴は、インターネットで繋がったP2Pネットワーク上で取引データが公開・共有・監視されており、一部のコンピューターで取引記録を改竄しても他のコンピューターとの多数決によって正しい取引データが選ばれ、記録の改ざんを防げることから、データの唯一性を担保することができること。
また取引記録を管理する大規模コンピューターなしで、特定の管理者なしで構成できる非中央集権型であることから、システムダウンせず低コストで運用することができるといわれている。

以下理解を深めるためにJSでブロックチェーンを作成する。

スクリプトの作成

まずはブロックチェーンを示すクラスを作成する。

// blockchain.js
module.exports = class Blockchain {
    constructor() {
        this.chain = [];
        this.pendingTransactions = [];
    }
}

chain:ブロックを格納する配列
pendingTransactions:未確定のトランザクション(ひとつの入金・出金の取引の記録)を格納する配列

ブロックチェーンにおけるトランザクションは一つの取引記録であり、複数のトランザクションがブロックに格納される。

次にブロックを生成するメソッドを作成し、それをテストしてみる。

// blockchain.js
module.exports = class Blockchain {
    constructor() {
        this.chain = [];
        this.pendingTransactions = [];
    }

    createNewBlock(nonce, previousBlockHash, hash) {
        const newBlock = {
            index: this.chain.length + 1,
            timestamp: Date.now(),
            transaction: this.pendingTransactions,
            nonce: nonce,
            hash: hash,
            previousBlockHash: previousBlockHash
        };

        this.pendingTransactions = [];
        this.chain.push(newBlock);

        return newBlock;
    }
}


// クラス機構が嫌であれば、こんな感じに書き換える
function Blockchain() {
  this.chain = [];
  this.pendingTransactions = [];
  this.createNewBlock(100, "0", "0");
}

Blockchain.prototype.createNewBlock = function(nonce, previousBlockHash, hash) {
...
};
// test.js
const Blockchain = require('./blockchain');
const bitcoin = new Blockchain();

bitcoin.createNewBlock(7653, "00KNWRUBWEJWENFOJNWO", "00NDGHEWNEJWHBRNMNWO");
bitcoin.createNewBlock(8971, "00HDNFHEWEDGRBCHRNKG", "00HDYENRHFBKDURNFHNE");
bitcoin.createNewBlock(9761, "00JOIRNNOIHWEOUBNEWO", "00NJKRUOQWNOIWHRNOWQ");

イメージとしては上記のようなチェーンになる。

次にトランザクションを作成する。

// blockchain.js
module.exports = class Blockchain {
〜中略〜
  createNewTransaction(amount, sender, recipient) {
      const newTransaction = {
          amount: amount,
          sender: sender,
          recipient: recipient
      }
  
      this.pendingTransactions.push(newTransaction);
  }
〜中略〜
}
// test
const Blockchain = require("./blockchain");
const bitcoin = new Blockchain();

bitcoin.createNewBlock(80103, "00KNWRUBWEJWENFOJNWO", "00NDGHEWNEJWHBRNMNWO");

bitcoin.createNewTransaction(
 1,
 "ALICEJSJSNWNN",
 "BOBDKENINOMDO"
);

console.log(bitcoin);

この時点ではトランザクションはまだブロックに含まれておらず、未確定の状態である。トランザクションはブロックをチェーンに追加する「マイニング」によって確定される。

// test
const Blockchain = require("./blockchain");
const bitcoin = new Blockchain();

bitcoin.createNewBlock(80103, "00KNWRUBWEJWENFOJNWO", "00NDGHEWNEJWHBRNMNWO");

bitcoin.createNewTransaction(
 1,
 "ALICEJSJSNWNN",
 "BOBDKENINOMDO"
);

// このタイミングでトランザクションが2番目のブロックに格納される
bitcoin.createNewBlock(99282, "00HDNFHEWEDGRBCHRNKG", "00HDYENRHFBKDURNFHNE");

console.log(bitcoin);

採掘について

まずsha256パッケージをインストールする。

$ npm install sha256

その後、ブロックにハッシュ関数をかけるメソッドを作成する。
引数を足し合わせてハッシュ関数をかける。

// blockchain.js
const sha256 = require('sha256');
module.exports = class Blockchain {
〜中略〜
  hashBlock(previousBlockHash, currentBlockData, nonce) {
      const dataAsString = previousBlockHash + nonce.toString() + JSON.stringify(currentBlockData);
      const hash = sha256(dataAsString);
      return hash;
  }
〜中略〜
}

previousBlockHashが前のブロックのハッシュで、currentBlockDataが現在のブロックのデータ、nonceはノンスである。
前のブロックのハッシュを現在のブロックの情報に加えており、前のブロックの情報と、現在のブロックの情報が合わさることになる。
つまり過去の情報を元に最新のハッシュを作っていくことになり、これが延々と続くのでハッシュが「チェーン」と言われる所以
である。

下記が動作テストのプログラムである。

// test.js
const Blockchain = require("./blockchain");
const bitcoin = new Blockchain();

const previousBlockHash = "0AA0IAIJIJUIGGUGUYG";

const currentBlockData = [
 {
   amount: 10,
   sender: "ALICE090970FYFFYFYFIF",
   recipient: "BOB797789790JFJFFGFJF"
 },
 {
   amount: 30,
   sender: "ALICGHIUGUGOOIGODYGDHFD",
   recipient: "BOBTYSHGHOUHOHOHOHOHO"
 },
 {
   amount: 200,
   sender: "ALICEHJGUGUTETEEUUCVVUVUV",
   recipient: "BOBGIUGIUGIUDRTESREAREUY"
 }
];

const nonce = 100;

console.log(bitcoin.hashBlock(previousBlockHash, currentBlockData, nonce));

Proof Of Work(Pow)

マイニングをする人のことをマイナーというが、そのマイナーが多くいるなかで、誰が最も早くノンスを見つけ報酬をもらえるかというマイニング競争なるものを行う。
これが Proof Of Work でハッシュ化を繰り返し行い検証することがマシンパワーを注ぎ込むということになり、マシンパワーを注ぎ込んだことが取引の正統性の担保になる。

今回は競争相手がいないため、ロジックのみを実装する。
また今回は、ハッシュの先頭が0000になるまでブロックのハッシュ化を繰り返すこととする。

// blockchain.js
const sha256 = require('sha256');
module.exports = class Blockchain {
〜中略〜
  proofOfWork(previousBlockHash, currentBlockData) {
    let nonce = 0;
    let hash = this.hashBlock(previousBlockHash, currentBlockData, nonce);
    while(hash.substring(0, 4) !== '0000') {
        nonce++;
        hash = this.hashBlock(previousBlockHash, currentBlockData, nonce);
    }
    return nonce;
  }
〜中略〜
}
// test
const Blockchain = require("./blockchain");
const bitcoin = new Blockchain();

const previousBlockHash = "0AA0IAIJIJUIGGUGUYG";

const currentBlockData = [
 {
   amount: 10,
   sender: "ALICE090970FYFFYFYFIF",
   recipient: "BOB797789790JFJFFGFJF"
 },
 {
   amount: 30,
   sender: "ALICGHIUGUGOOIGODYGDHFD",
   recipient: "BOBTYSHGHOUHOHOHOHOHO"
 },
 {
   amount: 200,
   sender: "ALICEHJGUGUTETEEUUCVVUVUV",
   recipient: "BOBGIUGIUGIUDRTESREAREUY"
 }
];

console.log(bitcoin.proofOfWork(previousBlockHash, currentBlockData));
// 105106

生成されたノンスをhashBlockに与えて正しいかを検証する。

// test.js
const Blockchain = require("./blockchain");
const bitcoin = new Blockchain();

const previousBlockHash = "0AA0IAIJIJUIGGUGUYG";

const currentBlockData = [
 {
   amount: 10,
   sender: "ALICE090970FYFFYFYFIF",
   recipient: "BOB797789790JFJFFGFJF"
 },
 {
   amount: 30,
   sender: "ALICGHIUGUGOOIGODYGDHFD",
   recipient: "BOBTYSHGHOUHOHOHOHOHO"
 },
 {
   amount: 200,
   sender: "ALICEHJGUGUTETEEUUCVVUVUV",
   recipient: "BOBGIUGIUGIUDRTESREAREUY"
 }
];

console.log(bitcoin.hashBlock(previousBlockHash, currentBlockData, 105106));
// 0000eaf3ccc12559217ff4c0786a422935f36823936be0a7343c3e6b0e0ba48b

先頭が0000となっていることから、正統性が確認できる。
また上記のプログラムから分かる通り、マイニングには時間がかかるが、検証は一瞬で終わる。

一連の流れをつかむ

一番最初のブロックである Genesis Block を生成する処理をコンストラクタにいれておき、一連の流れを見てみる。

// blockchain.js(全体)
const sha256 = require('sha256');

module.exports = class Blockchain {
    constructor() {
        this.chain = [];
        this.pendingTransactions = [];
        this.createNewBlock(100, '0', '0')
    }

    createNewBlock(nonce, previousBlockHash, hash) {
        const newBlock = {
            index: this.chain.length + 1,
            timestamp: Date.now(),
            transactions: this.pendingTransactions,
            nonce: nonce,
            hash: hash,
            previousBlockHash: previousBlockHash
        };

        this.pendingTransactions = [];
        this.chain.push(newBlock);

        return newBlock;
    }

    getLastBlock() {
        return this.chain[this.chain.length - 1];
    }

    createNewTransaction(amount, sender, recipient) {
        const newTransaction = {
            amount: amount,
            sender: sender,
            recipient: recipient
        }

        this.pendingTransactions.push(newTransaction);
    }

    hashBlock(previousBlockHash, currentBlockData, nonce) {
        const dataAsString = previousBlockHash + nonce.toString() + JSON.stringify(currentBlockData);
        const hash = sha256(dataAsString);
        return hash;
    }

    proofOfWork(previousBlockHash, currentBlockData) {
        let nonce = 0;
        let hash = this.hashBlock(previousBlockHash, currentBlockData, nonce);
        while(hash.substring(0, 4) !== '0000') {
            nonce++;
            hash = this.hashBlock(previousBlockHash, currentBlockData, nonce);
        }
        return nonce;
    }
}
// test.js
const Blockchain = require('./blockchain');
const bitcoin = new Blockchain();

bitcoin.createNewTransaction(
    100,
    "ALICE090970FYFFYFYFIF",
    "BOB797789790JFJFFGFJF"
);

function mining(bitcoin) {
    //前のブロックを取得
    const lastBlock = bitcoin.getLastBlock();

   //前のブロックハッシュを取得
    const previousBlockHash = lastBlock["hash"];

    //現在のブロックのデータ
    const currentBlockData = {
      transactions: bitcoin.pendingTransactions,
      index: lastBlock["index"] + 1
    };

    const nonce = bitcoin.proofOfWork(previousBlockHash, currentBlockData);
    console.log(nonce);

    const blockHash = bitcoin.hashBlock(
      previousBlockHash,
      currentBlockData,
      nonce
    );

    const newBlock = bitcoin.createNewBlock(nonce, previousBlockHash, blockHash);
};

mining(bitcoin);

bitcoin.createNewTransaction(
    200,
    "ALICE090970FYFFYFYFIF",
    "BOB797789790JFJFFGFJF"
);

mining(bitcoin);

bitcoin.createNewTransaction(
 300,
 "ALICE090970FYFFYFYFIF",
 "BOB797789790JFJFFGFJF"
);

mining(bitcoin);

console.log(bitcoin);

コンソール出力すると、現在のブロックのハッシュが次のブロックのpreviousBlockHashに入っていることが確認できる。
これでブロックが連結できたとみなすことができる。

P2Pネットワークはまたの機会に。。