ビットコイン ソースコード研究ノート (パート 3)

ビットコイン ソースコード研究ノート (パート 3)

第3章

この章では、いくつかの新しいデータ構造を紹介します。特に指定がない限り、この章で説明するすべてのクラスと関数は main.h または main.cpp にあります。

各ノードはブロックチェーンのコピーを保存します。ブロックチェーンは相互接続されたブロック (CBlock インスタンス) で構成されています。各ブロックには複数のトランザクション (CTransaction 機能) が含まれます。ブロックとトランザクション情報をメモリとディスクに保存、検索、読み取るために、Bitcoin ではいくつかのアクセス クラスが導入されています。これらには、ブロックのインデックス作成用の CBlockIndex と CDiskBlockIndex、トランザクションのインデックス作成用の CDiskTxPos と CTxIndex が含まれます。

CBlock

CBlock クラスはブロックを表します。

クラス CBlock
{
公共:
    //ヘッダ
    int バージョン;
    uint256 ハッシュ前ブロック;
    uint256 ハッシュMerkleRoot;
    符号なし整数nTime;
    符号なし整数nビット;
    符号なし整数nNonce;
    // ネットワークとディスク
    ベクトル<CTransaction> vtx;
    // メモリのみ
    変更可能なベクトル<uint256> vMerkleTree;
    CBlock()
    {
        SetNull();
    }
    シリアル化の実装
    (
        READWRITE(this->nVersion);
        nVersion = this->nVersion;
        READWRITE(ハッシュ前のブロック);
        READWRITE(ハッシュMerkleRoot);
        READWRITE(n時間);
        READWRITE(nビット);
        READWRITE(nNonce);
        // ConnectBlock は vtx が最後であることに依存しているため、オフセットを計算できます。
        if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY)))
            READWRITE(vtx);
        それ以外の場合 (fRead)
            const_cast<CBlock*>(this)->vtx.clear();
    )
    void SetNull()
    {
        バージョン = 1;
        ハッシュ前ブロック = 0;
        ハッシュマークルルート = 0;
        タイムスタンプ = 0;
        nビット = 0;
        0 の場合
        vtx.clear();
        vMerkleTree.clear();
    }
    ブールIsNull()定数
    {
        戻り値 (nBits == 0);
    }
    uint256 GetHash() 定数
    {
        Hash(BEGIN(nVersion), END(nNonce)) を返します。
    }
    uint256 BuildMerkleTree() 定数
    {
        vMerkleTree.clear();
        foreach(const CTransaction& tx, vtx)
            vMerkleTree.push_back(tx.GetHash());
        整数j = 0;
        (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) の場合
        {
            (int i = 0; i < nSize; i += 2)の場合
            {
                int i2 = min(i+1, nSize-1);
                vMerkleTree.push_back(ハッシュ(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]),
                                           BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2])));
            }
            j += nサイズ;
        }
        戻り値 (vMerkleTree.empty() ? 0 : vMerkleTree.back());
    }
    ベクトル<uint256> GetMerkleBranch(int nIndex) const
    {
        (vMerkleTree.empty()) の場合
            ビルドMerkleTree();
        ベクトル<uint256> vMerkleBranch;
        整数j = 0;
        (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) の場合
        {
            int i = min(nIndex^1, nSize-1);
            vMerkleBranch.push_back(vMerkleTree[j+i]);
            nインデックス>>=1;
            j += nサイズ;
        }
        vMerkleBranch を返します。
    }
    静的 uint256 CheckMerkleBranch(uint256 ハッシュ、const ベクトル<uint256>& vMerkleBranch、int nIndex)
    {
        (nIndex == -1)の場合
            0を返します。
        foreach(const uint256& otherside, vMerkleBranch)
        {
            (nIndex & 1)の場合
                hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash));
            それ以外
                hash = Hash(BEGIN(ハッシュ), END(ハッシュ), BEGIN(反対側), END(反対側));
            nインデックス>>=1;
        }
        ハッシュを返します。
    }
    ブールWriteToDisk(ブールfWriteTransactions、符号なしint&nFileRet、符号なしint&nBlockPosRet)
    {
        // 追加する履歴ファイルを開く
        CAutoFile ファイル出力 = AppendBlockFile(nFileRet);
        if (!fileout)
            エラーを返します("CBlock::WriteToDisk() : AppendBlockFile が失敗しました");
        if (!fWriteTransactions)
            fileout.nType |= SER_BLOCKHEADERONLY;
        // インデックスヘッダーを書き込む
        符号なし int nSize = fileout.GetSerializeSize(*this);
        ファイル出力 << FLATDATA(pchMessageStart) << nSize;
        // ブロックを書き込む
        nBlockPosRet = ftell(ファイル出力);
        (nBlockPosRet == -1)の場合
            エラーを返します("CBlock::WriteToDisk() : ftell が失敗しました");
        ファイル出力 << *this;
        true を返します。
    }
    bool ReadFromDisk(unsigned int nFile、unsigned int nBlockPos、bool fReadTransactions) 関数
    {
        SetNull();
        // 履歴ファイルを開いて読み取る
        CAutoFile ファイルイン = OpenBlockFile(nFile、nBlockPos、"rb");
        if (!filein)
            エラーを返します("CBlock::ReadFromDisk() : OpenBlockFile が失敗しました");
        if (!fReadTransactions)
            ファイルイン.nType |= SER_BLOCKHEADERONLY;
        // ブロックを読み込む
        ファイルイン >> *this;
        // ヘッダーをチェックする
        (CBigNum().SetCompact(nBits) > bnProofOfWorkLimit) の場合
            エラーを返します("CBlock::ReadFromDisk(): ブロック ヘッダーに nBits のエラーがあります");
        (GetHash() > CBigNum().SetCompact(nBits).getuint256()) の場合
            エラーを返します("CBlock::ReadFromDisk() : ブロック ヘッダー内の GetHash() エラー");
        true を返します。
    }
    void print() 定数
    {
        printf("CBlock(ハッシュ=%s、ver=%d、hashPrevBlock=%s、hashMerkleRoot=%s、nTime=%u、nBits=%08x、nNonce=%u、vtx=%d)\n",
            GetHash().ToString().substr(0,14).c_str()、
            nバージョン、
            ハッシュ前ブロック.ToString().substr(0,14).c_str(),
            hashMerkleRoot.ToString().substr(0,6).c_str(),
            n時間、nビット、nNonce、
            vtx.size());
        (int i = 0; i < vtx.size(); i++) の場合
        {
            printf(" ");
            vtx[i].print();
        }
        printf("vMerkleTree: ");
        (int i = 0; i < vMerkleTree.size(); i++) の場合
            printf("%s ", vMerkleTree[i].ToString().substr(0,6).c_str());
        printf("\n");
    }
    int64 GetBlockValue(int64 nFees) const;
    bool ブロッ​​クを切断します(CTxDB& txdb、CBlockIndex* pindex);
    ブール ConnectBlock(CTxDB& txdb, CBlockIndex* pindex);
    bool ReadFromDisk(const CBlockIndex* ブロックインデックス、bool fReadTransactions);
    bool AddToBlockIndex(unsigned int nFile、unsigned int nBlockPos);
    bool CheckBlock() 定数;
    ブール AcceptBlock();
};

ここでいくつか注意点があります:

  • ブロックには複数のトランザクションが含まれ、vector<CTransaction> vtx に格納されます。

  • GetHash() 関数によって生成されるブロックのハッシュは、ブロック データ全体ではなく、ブロックのブロック ヘッダーを計算することによって取得されます。具体的には、ハッシュはメンバー変数 nVersion から nNonce までから生成され、トランザクション コンテナ vtx は含まれません。

  • メンバー変数 uint256 hashMerkleRoot はブロック ヘッダーの一部です。これはメンバー関数 BuildMerkleTree() によって生成され、すべてのトランザクション vtx を入力パラメータとして受け取り、最終的に 256 ビットのハッシュ hashMerkleRoot を生成します。

  • uint256 hashPrevBlock は、ブロック ヘッダーに含まれる、現在のブロックの前のブロックのハッシュです。したがって、ブロックのハッシュは、ブロックチェーン内の前のブロックのハッシュと関連しています。

CBlock::BuildMerkleTree()

BuildMerkleTree() は Merkle ツリーを構築し、ルートを返します。リーフ ノードと中間ノードを含むツリーの各ノードは、uint256 ハッシュ値です。ツリーはフラット化され、vector<uint256> vMerkleTree に配置されます。 Merkle ツリーをフラット化するために、関数はまずレベル 0 のリーフ ノードを vMerkleTree に配置し、次にレベル 1 の中間ノードをその直後に配置し、ルート ノードに到達するまで上方向にプロセスを繰り返します。

25 行目の変数 j は、vMerkleTree の開始位置をインデックスして、ツリーの各レベルの最初のノードを配置するために使用されます。変数 nSize は、各レイヤーのノード数です。レイヤー 0 から開始し、nSize=vtx.size()、j=0 になります。

最も外側の for ループはツリーの各レベルを訪問します。各層のノード数は、前の層のノード数の半分です (nSize=(nSize+1)/2、行 26)。内部の for ループは、特定のレイヤー内の各ノード ペアを順番に訪問し、ポインター i は各ステップで 2 ずつ増加します。ノード ペア (i と i2) のハッシュがコンテナー vMerkleTree に追加されます (i2=i+1)。 i が最後のノード ペアに到達すると、nSize が奇数の場合は i=nSize-1、nSize が偶数の場合は i=nSize-2 になります。前者の場合、最後のノード (i2=i=nSize-1) は自身のコピーとノード ペアを形成します。

CBlock::GetMerkleBranch()

この関数は、Merkle ツリー ブランチを返します。 Merkle ツリー全体がコンテナー vMerkleTree にフラット化されました。パラメータnIndexはコンテナvMerkleTree[nIndex]のノードを指します。値の範囲は 0 から vtx.size() までです。したがって、nIndex は常にトランザクションのハッシュであるリーフ ノードを指します。

返されるMerkleツリーブランチには、vMerkleTre[nIndex]からツリーのルートまでのすべてのノードが含まれます。各層のノードがペアで一致して次の層のノードになる、Merkle ツリーを構築するプロセスを思い出してください。レベル0では、nIndexが偶数の場合、ノードvMerkleTree[nIndex]に関連付けられたノードはvMerkleTree[nIndex+1]になります。 nIndexが奇数の場合、関連付けられるノードはvMerkleTree[nIndex-1]になります。簡単にするために、vMerkleTree[nIndex]ノードをA0、そのコンパニオンノードをB0と呼びます。 A0 は B0 と結合され、最初のレイヤーに配置される別のノード A1 を生成します。レイヤー 1 では、A1 のコンパニオン ノードは B1 であり、この 2 つがペアになってレイヤー 2 でノード A2 が生成されます。このプロセスは、Merkle ルートに到達するまで繰り返されます。ノード [A0、A1、A2、...] は A0 からルート ノードへのパスを形成し、そのコンパニオン ノード [B0、B1、...] は返される Merkle ブランチを構成します。

コンパニオン ノードを見つけるために、関数はツリーの各レベルを走査します (4 行目)。各レベルで、ポインター nIndex は前のレベルの値と比較して半分になります。したがって、nIndex は常にパス ノード [A0、A1、A2、...] を指します。別のポインタ i は、46 行目で min(nIndex^1, nSize-1) に設定されます。OR 演算子 ^1 は、nIndex の最後のビットを反転しますが、他のビットは変更しません。これにより、i は常に nIndex が指すノードのコンパニオン ノード (たとえば、ノード [B0、B1、B2、...]) を指すようになります。

マークルツリーの目的は何なのかと疑問に思うかもしれません。トランザクションがブロックに含まれているかどうかをすばやく確認できます。以下で紹介させていただきます。

CBlock::CheckMerkleBranch()

この関数は、最初の引数ハッシュを受け取り、2 番目の引数 vMerkleBranch をそれと照合し、結果の Merkle ルートを返します。返されたルートが hashMerkleRoot と同じ場合、hash が指すトランザクションのハッシュが CBlock に含まれます。 3 番目のパラメータ nIndex は、vMerkleTree 内の最初のパラメータの位置です。これは GetMerkleBranch() の唯一のパラメータと同じです。

したがって、トランザクションが CBlock に含まれているかどうかを確認する必要がある場合は、トランザクションのハッシュを生成し、この関数を呼び出して、返された値が hashMerkleroot と同じであることを確認するだけです。

CBlock::WriteToDisk() および CBlock::ReadFromDisk()

WriteToDisk は CBlock をディスクに書き込みます。 80 行目では、AppendBlockFile() を呼び出します。

 FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode)
{
    (nFile == -1)の場合
        NULL を返します。
    ファイル* ファイル = fopen(strprintf("%s\\blk%04d.dat", GetAppDir().c_str(), nFile).c_str(), pszMode);
    if (!ファイル)
        NULL を返します。
    nBlockPos != 0 の場合 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w'))
    {
        (fseek(ファイル、nBlockPos、SEEK_SET) != 0)の場合
        {
            fclose(ファイル);
            NULL を返します。
        }
    }
    ファイルを返します。
}
静的符号なし整数nCurrentBlockFile = 1;
FILE* AppendBlockFile(符号なしint& nFileRet)
{
    0 を返します。
    ループ
    {
        FILE* ファイル = OpenBlockFile(nCurrentBlockFile, 0, "ab");
        if (!ファイル)
            NULL を返します。
        (fseek(ファイル, 0, SEEK_END) != 0)の場合
            NULL を返します。
        // FAT32 ファイルサイズは最大 4GB、fseek と ftell は最大 2GB なので、2GB 未満に抑える必要があります。
        (ftell(ファイル) < 0x7F000000 - MAX_SIZE)の場合
        {
            現在のブロックファイル。
            ファイルを返します。
        }
        fclose(ファイル);
        現在のブロックファイル++;
    }
}

AppendBlockFile() の最初のパラメータ nFileRet は、戻り値を格納するために使用されます。 AppendBlockFile() および OpenBlockFile() のソース コードを分析すると、nFileRet は「blk0001.data」および「blk0002.data」という形式で名前が付けられた一連のデータ ファイルであることがわかります。チャンクはこれらのファイルに保存されます。これらはブロック データ ファイルと呼ばれます。

WriteToDisk() に戻ります。 3 番目のパラメータ nBlockPosRet は、ブロック データ ファイル内の現在の CBlock の開始位置です。 Block::WriteToDisk() の 89 行目では、nBlockRet が ftell() の戻り値に割り当てられます。これは、ブロック データ ファイルの現在のポインター位置です。その後、現在の CBlock はシリアル化された形式でブロック データ ファイルに保存されます (行 92)。

ReadFromDisk() は非常に簡単に理解できます。

CBlockIndex と CDiskBlockIndex

上記の CBlock::WriteToDisk() および CBlock::ReadFromDisk() の分析は、ブロックが一連のブロック データ ファイルに格納されていることを示しています。 CBlockIndex は、このすべての情報を別のクラス CBlockIndex にカプセル化します。

 //
// ブロック チェーンはツリー状の構造で、
// ルートにジェネシスブロックがあり、各ブロックには複数の
// 次のブロックとなる候補。 pprevとpnextは、
// メイン/最長チェーン。ブロックインデックスには複数のpprevが後ろを指す場合がある
// しかし、pnextは最長のブランチのみを指すか、
// ブロックが最長チェーンの一部でない場合は null になります。
//
クラス CBlockIndex
{
公共:
    const uint256* phashブロック;
    CBlockIndex* 前のページ;
    CBlockIndex* pnext;
    符号なし int nFile;
    符号なし整数nブロック位置;
    int n高さ;
    //ブロックヘッダー
    int バージョン;
    uint256 ハッシュMerkleRoot;
    符号なし整数nTime;
    符号なし整数nビット;
    符号なし整数nNonce;
//.......
    uint256 GetBlockHash() 定数
    {
        *phashBlock を返します。
    }
//.......
};

nFile と nBlockPos に加えて、CBlockIndex には、それが指すブロック ヘッダーのコピーも含まれます (pprev->phashBlock を介してアクセスできるフィールド hashPrevBlock を除く)。これにより、ブロック データ ファイルからブロック全体を読み取ることなく、ブロック ハッシュを計算できるようになります。

コメント行 1 ~ 6 から、pprev はブロックチェーン内の前のブロック インデックスを指し、pnext はブロックチェーン内の次のブロック インデックスを指していることがわかります。

よく注意して見ると、「ブロックチェーン内のブロック」ではなく「ブロックチェーン内のブロックインデックス」と言っていることに気づくでしょう。これは少し混乱を招きます。結局のところ、ブロックチェーンはブロックインデックスではなくブロックで構成されていますよね?ブロックチェーンはブロックで構成されているというのは事実ですが、別の言い方をすれば、ブロックチェーンはブロックインデックスで構成されているとも言えます。つまり、ブロックの完全なデータはディスク上のブロック データ ファイルに保存され、ブロック インデックスはブロックのメンバー変数 nFile と nBlockPos を通じてブロックを参照します。ポインター pprev と pnext はそれぞれブロックの前のブロックと次のブロックを指し、ブロックチェーン全体を形成します。したがって、ブロックインデックスはブロックチェーンと同じくらい意味があります。

ブロックチェーンと呼ばれていますが、ビットコインのブロックチェーンは実際には線形構造ではなく、ツリー構造になっています。このツリーのルートは、ソース コードにハードコードされているジェネシス ブロックです。他のブロックはジェネシスブロックの上に蓄積されます。 1 つのブロックに 2 つ以上のブロックを蓄積することは合法です。これが起こると、ブロックチェーンはフォークします。ブロックチェーンには複数のフォークが含まれる場合があり、フォークされたブランチがさらにフォークする場合があります。

フォークにより、複数のブロック インデックスの pprev フィールドが同じ先行インデックスを指す場合があります。これらの各ブロック インデックスは分岐を開始し、これらの分岐は同じ先行ブロックに基づいています。ただし、先行ブロックの pnext フィールドは、最長の分岐を開始する後続ブロックのみを指すことができます。ツリーのルート (ジェネシス ブロック) から最長ブランチの最後のブロックまでのパスは、最長チェーン、ベスト チェーン、またはこのブロックチェーンのメイン チェーンと呼ばれます。

ポインター phashBlock はブロックのハッシュを指し、これは CBlockIndex に埋め込まれたブロック ヘッダーを通じてオンサイトで計算できます。

nHeight はブロックチェーン内のブロックの高さです。ブロックチェーンは、高さ 0 のブロック、つまりジェネシス ブロックから始まります。次のブロックの高さは 1 になります。

CBlockIndex インスタンスはメモリ内にのみ保存されます。ブロック インデックスをディスクに保存するために、派生クラス CDiskBlockIndex がここで定義されています。

クラス CDiskBlockIndex : パブリック CBlockIndex
{
公共:
    uint256 ハッシュ前;
    uint256 ハッシュ次;
    CDiskブロックインデックス()
    {
        ハッシュ前 = 0;
        ハッシュ次 = 0;
    }
    明示的な CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex)
    {
        hashPrev = (pprev ? pprev->GetBlockHash() : 0);
        hashNext = (pnext ? pnext->GetBlockHash() : 0);
    }
    シリアル化の実装
    (
        if (!(nType & SER_GETHASH))
            READWRITE(nバージョン);
        READWRITE(ハッシュ次);
        READWRITE(nファイル);
        READWRITE(nブロック位置);
        READWRITE(n高さ);
        //ブロックヘッダー
        READWRITE(this->nVersion);
        READWRITE(ハッシュ前);
        READWRITE(ハッシュMerkleRoot);
        READWRITE(n時間);
        READWRITE(nビット);
        READWRITE(nNonce);
    )
    uint256 GetBlockHash() 定数
    {
        CBlock ブロック;
        ブロック.nVersion = nVersion;
        ブロック.hashPrevブロック = hashPrev;
        ブロックのハッシュMerkleRoot = ハッシュMerkleRoot;
        ブロック.nTime = nTime;
        ブロック.nBits = nBits;
        ブロック.nNonce = nNonce;
        block.GetHash() を返します。
    }
//.......
};

このクラスには、前のブロック インデックスと後続のブロック インデックスのハッシュである hashPrev と hashNext という 2 つのメンバー変数があります。これらを CBlockIndex の pprev および pnext と混同しないでください。後者はブロック インデックスへのポインターですが、前者はブロックのハッシュです。

CBlockIndex も CDiskBlockIndex もハッシュを持ちません。これらは常に、指し示すブロックのハッシュによって自身を識別します。これは、CDiskBlockIndex のコンストラクター (行 15) から結論付けることができます。このコンストラクタでは、hashPrev が pprev->GetBlockHash() に割り当てられます。ここで、pprev は CBlockIndex へのポインタです。 CBlockIndex のメンバー関数 GetBlockHash を確認すると、先行ブロックのハッシュを返すことがわかります。 CDiskBlockIndex の場合、そのメンバー関数 GetBlockHash() も、それが指すブロックのハッシュを返します。

CBlockIndex にはシリアル化メソッドがありませんが、CDiskBlockIndex はマクロ IMPLEMENT_SERIALIZE を通じてシリアル化を実装します。これは、後者はディスクに保存する必要があるのに対し、前者はメモリ内にのみ存在するためです。

CMerkleTx と CWallet

Merkle ツリーと CBlock を理解した後、CTransaction から派生した他の 2 つの重要なクラス、CMerkleTx と CWallet を確認しましょう。

クラス CMerkleTx : パブリック CTransaction
{
公共:
    uint256 ハッシュブロック;
    ベクトル<uint256> vMerkleBranch;
    int インデックス;
    // メモリのみ
    変更可能なブール値 fMerkleVerified;
    CMerkleTx()
    {
        初期化();
    }
    CMerkleTx(const CTransaction& txIn) : CTransaction(txIn)
    {
        初期化();
    }
    void 初期化()
    {
        ハッシュブロック = 0;
        インデックス = -1;
        fMerkleVerified = false;
    }
    int64 GetCredit() 定数
    {
        // コインベースがチェーン内で安全に十分な深さになるまで待つ必要があります。
        (IsCoinBase() && GetBlocksToMaturity() > 0) の場合
            0を返します。
        CTransaction::GetCredit() を返します。
    }
    シリアル化の実装
    (
        nSerSize += SerReadWrite(s、*(CTransaction*)this、nType、nVersion、ser_action);
        nVersion = this->nVersion;
        READWRITE(ハッシュブロック);
        READWRITE(vMerkleBranch);
        READWRITE(nインデックス);
    )
    int SetMerkleBranch(const CBlock* pblock=NULL);
    int GetDepthInMainChain() 定数;
    bool IsInMainChain() 定数 { GetDepthInMainChain() > 0 を返します。 }
    int GetBlocksToMaturity() 定数;
    bool AcceptTransaction(CTxDB& txdb, bool fCheckInputs=true);
    bool AcceptTransaction() { CTxDB txdb("r"); AcceptTransaction(txdb) を返します。 }
};

CTransaction インスタンスが収集され、CBlock が生成されます。特定の CTransaction インスタンスの場合、その Merkle ブランチと CBlock の vector<CTranaction> vtx フィールド内の位置を使用して、インスタンスがこの CBlock に含まれているかどうかを確認できます。そのため、CMerkleTx には、Merkle ブランチ vMerkleBranch と位置インデックス nIndex という 2 つの追加フィールドが含まれています。 CMerkleTx インスタンスはこれら 2 つのフィールドを持ち、ブロックに属しているかどうかを簡単に確認できます。

uint256 hashBlock は、CBlock 内のこのトランザクションのハッシュです。

CWalletTx は CMerkleTx をさらに拡張して、所有者だけが関心を持つ情報を含めます。これらのメンバー フィールドについては、コード内で遭遇したときに説明します。

クラス CWalletTx : パブリック CMerkleTx
{
公共:
    ベクトル<CMerkleTx> vtxPrev;
    map<文字列, 文字列> mapValue;
    ベクトル<ペア<文字列、文字列> > vOrderForm;
    符号なし整数 fTimeReceivedIsTxTime;
    受信日時が符号なし整数である。 // このノードが受信した時間
    char fFromMe;
    char fSpent;
    //// 支払人からの注文であることがわかるように、注文情報に署名する必要があるでしょう
    // メモリのみ
    変更可能な符号なし整数 nTimeDisplayed;
//.......
};

CDiskTxPos と CTxIndex

これら 2 つのクラスは、トランザクションのインデックス作成に使用されます。

クラス CDiskTxPos
{
公共:
    符号なし int nFile;
    符号なし整数nブロック位置;
    符号なし整数nTxPos;
//.......
}
クラス CTxIndex
{
公共:
    CDiskTxPos 位置;
    ベクトル<CDiskTxPos> vSpent;
}

CDiskTxPos の nFile、nBlockPos、nTxPos は、ブロック データ ファイルのシリアル番号、ブロック データ ファイル内のブロックの位置、ブロック データ内のトランザクションの位置です。したがって、CDiskTxPos には、ブロック データ ファイルからトランザクションを見つけるためのすべての情報が含まれています。

CTxIndex は、トランザクションと、トランザクション出力を使用するトランザクションのディスク上の場所を含むデータベース レコードです。トランザクションはソース トランザクションまで簡単に遡ることができます (第 1 章を参照) が、その逆はできません。 vector<CDiskTxPos> vSpent には、CDiskTxPos pos が指すトランザクションをソース トランザクションとして持つすべてのトランザクションの位置、つまり、CDiskTxPos pos が指すトランザクションを使用するすべてのトランザクションの位置が含まれます。

<<:  SECが再考請願を承認し、ビットコインETFに希望が戻った

>>:  ライトコインの創設者は、Segregated Witness を有効化し、マイナーにそうするよう説得する予定

推薦する

Amazon クラウド サービスの停止: Coinbase、dYdX などが影響を受ける

12月7日(米国東部時間)、アマゾンのクラウドサービスで大規模な障害が発生し、一部の関連ウェブサイト...

イーサリアムマイニングが人気になってきました。一般の人はどうやって市場に参入すればいいのでしょうか?

さまざまなDeFiプロジェクトの流動性マイニングインセンティブメカニズムのおかげで、8月のイーサリア...

アリアンツの経済学者:ビットコインは消滅しないが価格は半分になるはずだ

9月13日、アリアンツのチーフエコノミスト、エリアン氏はCNBCのインタビューで「ビットコインの価格...

暗号通貨マイニング会社がバフェット氏をターゲットにした嘲笑広告を掲載

暗号通貨マイニング会社は、ウォーレン・バフェット氏のオマハ事務所近くに出現した最新の看板の作成者は自...

イーサリアムフェーズ3アップグレード進捗:「メトロポリス」の建設が半分完了、イーサリアムはさらに便利に

イーサリアムは最も困難な時期を乗り越え、計画されている4つの開発フェーズのうち3番目のフェーズに向か...

Yahoo Financeが暗号通貨ニュースを完全統合

Yahoo Financeは暗号通貨関連のニュースを完全に統合し始めており、メニューバーにビットコイ...

Ethereum がフォークすると、NFT も「フォーク」するのでしょうか?

Ethereum のPoSからPoWへのプロセスは、マイナーの利益に影響を与えます。合併日が近づく...

英国政府科学局がブロックチェーン技術の開発に関する提言を発表

英国政府科学局は、英国政府がブロックチェーンと分散型台帳技術を研究し実験することを推奨する報告書を発...

ビットコイン採掘機メーカー第3位のエバン・インターナショナルは米国で株式公開を計画しており、昨年の粗利益は黒字から赤字に転じた。

世界第3位のビットコイン採掘機メーカーであるエバン・インターナショナルは、香港での上場を2度試みて失...

CreditEaseはP2P企業が世界的な注目を集めるのを支援します

クレイジーな解説:中国の大手P2P融資および金融管理会社であるCreditEaseのCEOであるTa...

創設者は逃亡の疑いがあり、トルコの暗号通貨取引所Thodexは数億ドルを詐取した疑いがある

この記事はブルームバーグからのもので、原著者はタイラン・ビルギッチとフィラット・コゾクです。翻訳者:...

小売マイナーは悲惨な思いをしている。約束された分散化はどこにあるのだろうか?

この記事はCointelegraphから引用したもので、原著者はウィル・ヒースマンです。 Odail...

伝説の投資家チャーリー・マンガーは亡くなる前に暗号通貨と AI をどのように見ていたのでしょうか?

マンガー氏は、バークシャー・ハサウェイの副会長としての役割と、ウォーレン・バフェット氏との緊密な協力...

李小来が詐欺だと非難したPiコイン:メインネットなしで携帯電話の「マイニング」、ビットコインを空想として利用

BTCが上昇する中、「πコイン」Pi Networkのウォレット内部テストのニュースが出て、参加者の...