生トランザクションのシリアライゼーション

ビットコイン・トランザクションを生16進にシリアライズする方法 - バージョン、インプット、アウトプット、そしてlocktimeを正しいバイト順で。

0ステップ ·

進捗 0/0

何を学ぶか

  • ビットコイン・トランザクションの完全なシリアライゼーション形式
  • リトル・エンディアン・エンコーディングとビットコインがそれを使う理由
  • すべてのフィールドを単一の16進文字列に組み立てる方法

トランザクション形式の概要

シリアライズされたビットコイン・トランザクションは、厳格な構造のバイト列です。**レガシー(非SegWit)**トランザクションのレイアウト:

[Version] [Input Count] [Inputs...] [Output Count] [Outputs...] [Locktime]

SegWitトランザクションの場合、二つの追加フィールドが挿入されます:

[Version] [Marker] [Flag] [Input Count] [Inputs...] [Output Count] [Outputs...] [Witness...] [Locktime]

各フィールドを順に見ていきましょう。

バージョン(4バイト)

トランザクション・バージョン番号。ほぼすべての現代のトランザクションはバージョン2を使います(BIP68による相対タイムロックを有効にする):

02000000

これはリトル・エンディアンで0x00000002です。バージョン1(01000000)はまだ有効ですが、BIP68のシーケンス・ベースのタイムロックをサポートしません。

MarkerとFlag(SegWit専用、2バイト)

SegWitトランザクションはバージョンの後にマーカー・バイト(0x00)とフラグ・バイト(0x01)を挿入します。これらはパーサーにウィットネス・データが続くことを伝えます:

0001

マーカーは必ず0x00でなければなりません。フラグは必ず0以外でなければなりません(現時点では常に0x01)。

インプット数(VarInt)

インプット数を示す可変長整数。1~252個のインプットの場合、これは単一バイトです:

01  →  1 input
02  →  2 inputs
fd  →  followed by 2-byte little-endian count (for 253+)

シリアライズされたインプット

各インプットはステップ3で説明した通りにシリアライズされます:

[Previous TX Hash: 32 bytes]
[Output Index: 4 bytes]
[ScriptSig Length: VarInt]
[ScriptSig: variable]
[Sequence: 4 bytes]

署名前のSegWitインプットでは、ScriptSigは空です(長さ = 00)。

アウトプット数(VarInt)

インプット数と同じエンコーディング。2-アウトプットのトランザクションの場合:

02

シリアライズされたアウトプット

ステップ4で説明した各アウトプット:

[Value: 8 bytes]
[ScriptPubKey Length: VarInt]
[ScriptPubKey: variable]

ウィットネス・データ(SegWit専用)

ウィットネス・セクションはすべてのアウトプットの後、locktimeの前に現れます。各インプットは対応するウィットネス・スタックを持ちます。ステップ8で詳しく扱いますが、構造は:

[Stack item count: VarInt]
[Item 1 length: VarInt] [Item 1 data]
[Item 2 length: VarInt] [Item 2 data]
...

P2WPKHインプットの場合、ウィットネスは二つのアイテムを含みます:署名と公開鍵。

Locktime(4バイト)

locktimeフィールドは、トランザクションがブロックに含まれ得る最も早い時刻またはブロック高を指定します:

00000000  →  No locktime (can be mined immediately)

値が500,000,000未満ならブロック高として解釈されます。そうでなければUnixタイムスタンプです。ほとんどのトランザクションは0または現在のブロック高を使います(fee sniping - マイナーのインセンティブ攻撃を防ぐため)。

リトル・エンディアン・エンコーディング

ビットコインのシリアライゼーションはほとんどの複数バイト整数にリトル・エンディアンのバイト順を使います。これは最下位バイトが最初に来るということを意味します:

Decimal 500,000 = 0x0007A120

Big-endian:    0007A120
Little-endian: 20A10700

例外は前のトランザクション・ハッシュで、生の256ビット値として保存されます - しかしtxidは伝統的にビッグ・エンディアンで表示されるため、シリアライズされたトランザクションに挿入するときはバイト順を反転する必要があります。

完全な例

ここに1-インプット、2-アウトプットのSegWitトランザクション(署名前):

02000000                      # Version 2
0001                          # SegWit marker + flag
01                            # 1 input
  3f4fa198...7b1eabe0         # Previous TX hash (32 bytes, reversed)
  00000000                    # Output index 0
  00                          # Empty ScriptSig
  fdffffff                    # Sequence (RBF enabled)
02                            # 2 outputs
  e093040000000000            # Output 0: 300,000 sats
  160014<recipient-hash>      # P2WPKH ScriptPubKey
  b882020000000000            # Output 1: 198,590 sats (change)
  160014<change-hash>         # P2WPKH ScriptPubKey
                              # Witness data (empty until signing)
00000000                      # Locktime 0

この16進ブロブはまだ有効ではありません - 署名が必要です。それが次です。

次のステップ

トランザクションに署名するに進み、使用を承認する暗号学的証明を作成する方法を学びましょう。