– トランザクションによりビットコインは当事者から別の当事者へと転送する
– トランザクションは署名によりアンロックする
– ブロックはトランザクションを整列し、それ以降のトランザクションを無効にする 二重支払いを防ぐ
– 10分ごとにまとまった単位でトランザクションの集まりを決済する
### コインベーストランザクション
– インプットは1つのみ
– インプットの前のトランザクションIDは32バイトの00
– トランザクションインデックスはffffffff
def is_coinbase(self): if len(self.tx_ins) != 1: return False first_input = self.tx_ins[0] if first_input.prev_tx != b'\x00' * 32: return False if first_input.prev_index != 0xffffffff: return False return True
ScriptSig
from io import BytesIO from script import Script stream = BytesIO(bytes.fromhex('4d04ffff001d0104455468652054696d6573203033\ 2f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64\ 206261696c6f757420666f722062616e6b73')) s = Script.parse(stream) print(s.cmds[2])
$ python3 main.py
b’The Times 03/Jan/2009 Chancellor on brink of second bailout for banks’
BIP0034
コインベーストランザクションのScriptSigの先頭要素を規定
from io import BytesIO from script import Script from helper import little_endian_to_int stream = BytesIO(bytes.fromhex('5e03d71b07254d696e656420627920416e74506f6f\ 6c20626a31312f4542312f4144362f43205914293101fabe6d6d678e2c8c34afc36896e7d94028\ 24ed38e856676ee94bfdb0c6c4bcd8b2e5666a0400000000000000c7270000a5e00e00')) script_sig = Script.parse(stream) print(little_endian_to_int(script_sig.cmds[0]))
def coinbase_height(self): if not self.is_coinbase(): return None element = self.tx_ins[0].script_sig.cmds[0] return little_endian_to_int(element)
### ブロックヘッダー
ブロックヘッダーはブロックに含まれるトランザクションに関するメタデータ
– Version, Previous Block, Merkle Root, Timestamp, Bits, Nonce
block_id
from helper import hash256 block_hash = hash256(bytes.fromhex('020000208ec39428b17323fa0ddec8e887b4a7\ c53b8c0a0a220cfd0000000000000000005b0750fce0a889502d40508d39576821155e9c9e3f5c\ 3157f961db38fd8b25be1e77a759e93c0118a4ffd71d'))[::-1] block_id = block_hash.hex() print(block_id)
@classmethod def parse(cls, s): version = little_endian_to_int(s.read(4)) prev_block = s.read(32)[::-1] merkle_root = s.read(32)[::-1] timestamp = little_endian_to_int(s.read(4)) bits = s.read(4) nonce = s.read(4) return cls(version, prev_block, merkle_root, timestamp, bits, nonce) def serialize(self): result = int_to_little_endian(self.version, 4) result += self.prev_block[::-1] result += self.merkle_root[::-1] result += int_to_little_endian(self.timestamp, 4) result += self.bits result += self.nonce return result def hash(self): s = self.serialize() sha = hash256(s) return sha[::-1]
### バージョン
from io import BytesIO from block import Block b = Block.parse(BytesIO(bytes.fromhex('020000208ec39428b17323fa0ddec8e887b\ 4a7c53b8c0a0a220cfd0000000000000000005b0750fce0a889502d40508d39576821155e9c9e3\ f5c3157f961db38fd8b25be1e77a759e93c0118a4ffd71d'))) print('BIP9: {}'.format(b.version >> 29 == 0b001)) print('BIP91: {}'.format(b.version >> 4 & 1 == 1)) print('BIP141: {}'.format(b.version >> 1 & 1 == 1))
class Block
def bip9(self): return self.version >> 29 == 0b001 def bip91(self): return self.version >> 4 & 1 == 1 def bip141(self): return self.version >> 1 & 1 == 1
マークルルート: トランザクションを32バイトのハッシュにエンコード
タイムスタンプ: ロックタイムと新しいビット/ターゲット/ディフィカルティを計算する際に使用
ビット: ブロックのProof-of-workに必要な値をエンコーディングするフィールド
ノンス: 一度だけ使われる数値 number used only once
### Proof-of-Work
from helper import hash256 block_id = hash256(bytes.fromhex('020000208ec39428b17323fa0ddec8e887b4a7c5\ 3b8c0a0a220cfd0000000000000000005b0750fce0a889502d40508d39576821155e9c9e3f5c31\ 57f961db38fd8b25be1e77a759e93c0118a4ffd71d'))[::-1] print('{}'.format(block_id.hex()).zfill(64))
マイナーはノンスフィールドを自由に変える事でブロックヘッダーのハッシュを変更できる
ビットコインの全てのブロックヘッダーのハッシュが一定のターゲットを下回らなければならない
ビットフィールの一つは指数(exponent)で最後の1バイト、2つめは係数(coefficient)で残りのリトルエンディアンでエンコードした数値
target = coefficient x 256eponent-3
– ターゲットを計算する方法
from helper import little_endian_to_int bits = bytes.fromhex('e93c0118') exponent = bits[-1] coefficient = little_endian_to_int(bits[:-1]) target = coefficient * 256**(exponent - 3) print('{:x}'.format(target).zfill(64))
ブロックヘッダーのハッシュで、リトルエンディアンの整数として解釈した時に、ターゲットの数値を下回ったもの
from helper import little_endian_to_int, hash256 bits = bytes.fromhex('e93c0118') exponent = bits[-1] coefficient = little_endian_to_int(bits[:-1]) target = coefficient * 256**(exponent - 3) proof = little_endian_to_int(hash256(bytes.fromhex('020000208ec39428b17323\ fa0ddec8e887b4a7c53b8c0a0a220cfd0000000000000000005b0750fce0a889502d40508d3957\ 6821155e9c9e3f5c3157f961db38fd8b25be1e77a759e93c0118a4ffd71d'))) print(proof < target)
helper.py
def bits_to_target(bits): exponent = bits[-1] coefficient = little_endian_to_int(bits[:-1]) return coefficient * 256**(exponent - 3)
– difficulty
ターゲットに反比例する
difficulty = 0xffff x 256^0x1d-3 / target
from helper import little_endian_to_int bits = bytes.fromhex('e93c0118') exponent = bits[-1] coefficient = little_endian_to_int(bits[:-1]) target = coefficient * 256**(exponent - 3) difficulty = 0xffff * 256**(0x1d-3) / target print(difficulty)
def difficulty(self): lowest = 0xffff * 256**(0x1d - 3) return lowest / self.target() def check_pow(self): sha = hash256(self.serialize()) proof = little_endian_to_int(sha) return proof < self.target()
difficulty adjustment period
new_target = previous_target * time_differential / two week
ブロックは10分ごとのブロックに収束する
from io import BytesIO from block import Block from helper import TWO_WEEKS last_block = Block.parse(BytesIO(bytes.fromhex('00000020fdf740b0e49cf75bb3\ d5168fb3586f7613dcc5cd89675b0100000000000000002e37b144c0baced07eb7e7b64da916cd\ 3121f2427005551aeb0ec6a6402ac7d7f0e4235954d801187f5da9f5'))) first_block = Block.parse(BytesIO(bytes.fromhex('000000201ecd89664fd205a37\ 566e694269ed76e425803003628ab010000000000000000bfcade29d080d9aae8fd461254b0418\ 05ae442749f2a40100440fc0e3d5868e55019345954d80118a1721b2e'))) time_differential = last_block.timestamp - first_block.timestamp if time_differential > TWO_WEEKS * 4: time_differential = TWO_WEEKS * 4 if time_differential < TWO_WEEKS // 4: time_differential = TWO_WEEKS // 4 new_target = last_block.target() * time_differential // TWO_WEEKS print('{:x}'.format(new_target).zfill(64))
def target_to_bits(target): raw_bytes = target.to_bytes(32, 'big') raw_bytes = raw_bytes.lstrip(b'\x00') if raw_bytes[0] > 0x7f: exponent = len(raw_bytes) + 1 coefficient = b'\x00' + raw_bytes[:2] else: exponent = len(raw_bytes) coefficient = raw_bytes[:3] new_bits = coefficient[::-1] + bytes([exponent]) return new_bits
from io import BytesIO from block import Block from helper import TWO_WEEKS from helper import target_to_bits block1_hex = '000000203471101bbda3fe307664b3283a9ef0e97d9a38a7eacd88000000\ 00000000000010c8aba8479bbaa5e0848152fd3c2289ca50e1c3e58c9a4faaafbdf5803c5448dd\ b845597e8b0118e43a81d3' block2_hex = '02000020f1472d9db4b563c35f97c428ac903f23b7fc055d1cfc26000000\ 000000000000b3f449fcbe1bc4cfbcb8283a0d2c037f961a3fdf2b8bedc144973735eea707e126\ 4258597e8b0118e5f00474' first_block = Block.parse(BytesIO(bytes.fromhex(block1_hex))) last_block = Block.parse(BytesIO(bytes.fromhex(block2_hex))) time_differential = last_block.timestamp - first_block.timestamp if time_differential > TWO_WEEKS * 4: time_differential = TWO_WEEKS * 4 if time_differential < TWO_WEEKS // 4: time_differential = TWO_WEEKS // 4 new_target = last_block.target() * time_differential // TWO_WEEKS new_bits = target_to_bits(new_target) print(new_bits.hex())
$ python3 main.py
308d0118
def calculate_new_bits(previous_bits, time_differential): if time_differential > TWO_WEEKS * 4: time_differential = TWO_WEEKS * 4 if time_differential < TWO_WEEKS // 4: time_differential = TWO_WEEKS // 4 new_target = bits_to_target(previous_bits) * time_differential // TWO_WEEKS return target_to_bits(new_target)
加速してきた