Published Date : 2021年7月28日4:56

010 Pythonでビットコインを学ぶ (マイニング ノンス 4)
010 Use python to learn bitcoin (mining nonce 4)


This blog has an English translation


ニコニコ動画にアップした動画のまとめ記事です。

This is a summary blog post about a video I uploaded to NicoNico.

細かい部分は動画を参考にしてください。

Please refer to the video for details.


目次

Table of Contents




① 動画の説明
① Video Description



前回の続きです。

Continued from last time.

ちなみにtimeについてはUNIX時間でUTC(協定世界時)を使用しています。

As for time, UTC (Coordinated Universal Time) is used as UNIX time.

UNIX時間やUTCの詳細については各自で調べてください。

Please check the details of UNIX time and UTC by yourself.

簡単に言うとUNIX時間は1970年1月1日午前0時0分0秒からの経過秒数です。

Briefly, UNIX time is the number of seconds elapsed since 0:00:00 AM on January 1, 1970.

そしてUTC(協定世界時)はその名の通り世界で標準時刻としている時間のことです。

And UTC (Coordinated Universal Time), as the name suggests, is the standard time in the world.

ではdatetimeモジュールを使って確かめてみましょう。

Let's examine it using the datetime module.

同様に690000番目のブロックの時間を使用します。

Similarly, use the time in the 690000th block.

from datetime import datetime, timezone
import time

dt = datetime(2021, 7, 7, 7, 43, 7)
dt
datetime.datetime(2021, 7, 7, 7, 43, 7)
dt.replace(tzinfo=timezone.utc).timestamp()
1625643787.0

UTC(協定世界時)での690000ブロックのタイムスタンプ。

Timestamp for the 690000th block in UTC.

datetime.utcfromtimestamp(1625643787)
datetime.datetime(2021, 7, 7, 7, 43, 7)

UTC+9(日本時間)での690000ブロックのタイムスタンプ。標準時間よりプラス9時間進んでいます。

Timestamp for the 690000th block in UTC+9 (Japan time). It's 9 hours ahead of the standard time.

なので、ヘッダーの値を作成する時は注意してください。

So be careful when creating header values.

datetime.fromtimestamp(1625643787)
datetime.datetime(2021, 7, 7, 16, 43, 7)

dt.replace(tzinfo=timezone.utc).timestamp()
1625643787.0
time.mktime(dt.timetuple())
1625611387.0

dt.replace(tzinfo=timezone.utc).timestamp() == time.mktime(dt.timetuple())
False

time.mktime(dt.timetuple()) + (60 * 60 * 9) == dt.replace(tzinfo=timezone.utc).timestamp()
True

ちなみに16進数はPythonオブジェクトではint型で認識されているので、どちらを使っても構いません。

By the way, hexadecimal notation is recognized by Python objects as type int, so you can use either.

timestamp = 1231660825
hex(timestamp)
'0x4969a719'
timestamp == 0x4969a719
True
type(0x4969a719)
int

次に目標値を目安としてノンスを見つけてみましょう。

Next, let's find Nonce using the target value as a guide.

今度は難易度が1の100番目のブロックで検証してみましょう。

Let's try the 100th block of difficulty 1.

既にノンスは求められていますが、ループ処理でどれだけの時間がかかるかを検証します。

Already computed the nonce for this block, but I want to verify how long it takes to loop.

nonce = 1573057331

時間を計測したいのでTimeモジュールからTimeメソッドをインポートします。

I want to measure time, so I import the Time method from the Time module.

import codecs
import struct
from hashlib import sha256
from time import time

前回の時と同様に目標値を求める関数を作成しておきましょう。

Create a function that calculates the target value as you did for my previous video.

def calc_target(bits):
    idx = bits >> 24
    coef = bits & 0xffffff
    target_hexstr = '%064x' % (coef * (1 << (8*(idx - 3))))
    target_bytes = codecs.decode(target_hexstr, "hex")
    return target_bytes

ハッシュ値を算出する関数を作成しておきます。

Create a function that calculates the hash value.

def calc_hash(header):
    return sha256(sha256(header).digest()).digest()

ノンスを検索する関数を作成しましょう。引数はヘッダーと目標値のバイト列です。

Let's create a function that searches for a nonce. The arguments are the header and the target byte string.

計算時間は10分以内を目安にしますが、今回は1分に設定します。

Set the calculation time to 10 minutes or less, but this time set it to 1 minute.

ノンスを初期化します。

Initializes the nonce.

既にノンスの値は分かっているので、3分程度でノンスが見つかるように設定しました。取り敢えず最初は0から試してみましょう。

I already know the nonce value, so I set it to find the nonce in about 3 minutes. For now, let's try from 0.

計算開始時間を設定します。

Set the calculation start time.

計算を開始します。

Start the calculation.

ループ中にプリント関数で値を表示すると、実行速度が遅くなるので、コメントアウトしました。

It was commented out because the execution speed would be slow if the print function displays the value during the loop.

途中のノンスとハッシュ値をリアルタイムで見たい人はコメントを外してください。

If you want to see the nonce and hash values in the middle while running this script, uncomment them.

後は前回の動画の時のスクリプトを少し変化させただけです。

The rest of the script just slightly changed the script from my previous video.

計算時間の負荷を下げる為に、予めノンス以外のバイト値を連結させて、ヘッダーバイトを作成しておきます。

In order to reduce the computation time load, I decided to pre-concatenate the required values other than the nonce value to create a header byte.

if __name__ == '__main__':
    version = 1
    previousblockhash = "00000000cd9b12643e6854cb25939b39cd7a1ad0af31a9bd8b2efe67854b1995"
    merkleroot = "2d05f0c9c3e1c226e63b5fac240137687544cf631cd616fd34fd188fc9020866"
    time_stamp = 1231660825
    bits = 0x1d00ffff

    # 計算時間の負荷を下げる為に、予めノンス以外のバイト値を連結させて、ヘッダーバイトを作成しておきます。
    # In order to reduce the computation time load, I decided to pre-concatenate the required values other than the nonce value to create a header byte.
    header = b"".join([struct.pack("<L", version), codecs.decode(previousblockhash, "hex")[::-1],
                       codecs.decode(merkleroot, "hex")[::-1], struct.pack("<LL", time_stamp, bits)])

    target_bytes = calc_target(bits)
    nonce, hash_value = find_nonce(header, target_bytes)
    print("nonce ->", nonce)
    print("hash  ->", '%064x' % (int.from_bytes(hash_value, 'little')))

それではスクリプトを実行して試してみましょう。

Now let's run the script and try it out.

大体このスクリプトでは一秒間に50万回の計算を行っています。

This script does about 500,000 calculations per second.

ではプリント関数のコメントを外してもう一度スクリプトを実行してみましょう。

Let's uncomment the print function and run the script again.

ご覧の通り実行速度が低下しています。

As you can see, the execution speed is slow.

今度はすぐノンスが見つかるように先ほどのノンスの値の初期値をコメントアウトして使ってみましょう。

Now try uncommenting the initial nonce value and running the script again to find the nonce immediately.

大体このスクリプトでは10分間の中で約3億回計算できるので、丁度オフセットを3億程度にすれば15台くらいPCがあればどれか一つはノンスを10分以内に見つけることができるでしょう。

Roughly speaking, this script can calculate about 300 million times in 10 minutes, so if you just set the offset to 300 million or so, and have about 15 PCs, one of them will find a nonce in 10 minutes.

0x100000000 / 10 / 500000
858.9934592000001

(0x100000000 / 10 / 500000) / 60
14.316557653333335

(0x100000000 / 12 / 500000) / 60
11.93046471111111

(0x100000000 / 15 / 500000) / 60
9.544371768888888

0x100000000 / 15
286331153.06666666

このようにしてノンスを見つけることができたノードは前のブロックに次のブロックを繋げることができます。

A node that finds a nonce in this way can concatenate the next block to the previous one.

その時その計算リソースを提供した対価としてノンスを見つけたノードはコインを受け取ることができます。

The node that finds the nonce as a reward for providing the computing resource can receive the coin.

このブロックを長く繋げていけば、取引を偽ったりすることが困難になります。

The more blocks concatenated, the harder it is to disguise the transaction.

途中で改竄された取引のブロックを挿入しても、次のハッシュ値とノンスの値がまったく異なってしまうからです。

If you insert a block of transactions that were tampered with in the middle, the next hash value will be completely different from the nonce value.

この取引記録のブロックは分散型のネットワークを使って大勢のノードが監視しています。

These blocks of transaction records are monitored by many nodes using a distributed network.

しかし、たまにブロックが分岐してしまうことがあります。

However, the connections between these blocks sometimes branch.

ブロードキャストされるタイミングやネットワーク回線の問題によって各ノードが受け取るブロックに多少の時差が生じるからです。

This is because there is a slight time difference in the blocks each node receives due to broadcast timing and network line issues.

そうなった場合、一旦分岐されたブロックの繋がりも記録しておいて、時間が経ち、より長くブロックが繋がっているほうを信用するようにします。

In that case, record the connection of the blocks that were once branched and trust the block that has been connected for a longer time.

これらの作業をネットワークに繋がれた各ノードが行っていき、取引の信用性を保っています。

These tasks are performed by each node connected to the network to maintain the reliability of transactions.

しかし、それらの作業の大部分はソフトウェアが行っているので、人間がそれらの作業を意識することは殆どありません。

But most of those tasks are done by software, so humans are rarely aware of them.



以上です。お疲れ様です。

That's all. Thank you for your hard work.