blockchain

import hashlib

class Block():
    def __init__(self, data, prev_hash):
        self.index = 0
        self.nonce = 0
        self.prev_hash = prev_hash
        self.data = data

    def blockhash(self):
        blockheader = str(self.index) + str(self.prev_hash) + str(self.data) + str(self.nonce)
        block_hash = hashlib.sha256(blockheader.encode()).hexdigest()
        return block_hash

    def __str__(self):
        return "Block Hash: " + self.blockhash() + "\nPrevious Hash: " + self.prev_hash + "\nindex: " + str(self.index) + "\nData: " + str(self.data) + "\nNonce: " + str(self.nonce) + "\n--------------"

class Hashchain():
    def __init__(self):
        self.chain = ["0000000000000000000000000000000000000000000000000000000000000000"]

    def add(self, hash):
        self.chain.append(hash)

hashchain = Hashchain()
target = 0x777777 * 2**(8*(0x1e - 0x03))

for i in range(30):
    block = Block("Block " + str(i+1), hashchain.chain[-1])
    block.index = block.index + i + 1
    for n in range(4294967296):
        block.nonce = block.nonce + n
        if int(block.blockhash(), 16) < target:
            print(block)
            hashchain.add(block.blockhash())
            break

Proof of work

簡易的なhash計算

import hashlib

input_text = "satoshi"

for nonce in range(20):
    input_data = input_text + str(nonce)
    hash = hashlib.sha256(input_data.encode("UTF-8")).hexdigest()
    print(input_data + " → " + hash)

ブロックヘッダのdifficulty bitsにハッシュ値の条件が書かれている
0x1e777777 のような形式で記述されている。

# difficulty_bits = 0x1e777777
# exponent = 0x1e
# coefficient = 0x777777

target = 0x777777 * 2**(8*(0x1e - 0x03))
print(target)

target_hex = hex(target)[2:].zfill(64)
print(target_hex)
import hashlib

class Block():
    def __init__(self, data, prev_hash):
        self.index = 0
        self.nonce = 0
        self.prev_hash = prev_hash
        self.data = data

    def blockhash(self):
        blockheader = str(self.index) + str(self.prev_hash) + str(self.data) + str(self.nonce)
        block_hash = hashlib.sha256(blockheader.encode()).hexdigest()
        return block_hash

    def __str__(self):
        return "Block Hash: " + self.blockhash() + "\nPrevious Hash: " + self.prev_hash + "\nindex: " + str(self.index) + "\nData: " + str(self.data) + "\nNonce: " + str(self.nonce) + "\n--------------"

class Hashchain():
    def __init__(self):
        self.chain = ["0000000000000000000000000000000000000000000000000000000000000000"]

    def add(self, hash):
        self.chain.append(hash)

hashchain = Hashchain()
target = 0x777777 * 2**(8*(0x1e - 0x03))

for i in range(30):
    block = Block("Block " + str(i+1), hashchain.chain[-1])
    block.index = block.index + i + 1
    for n in range(4294967296):
        block.nonce = block.nonce + n
        if int(block.blockhash(), 16) < target:
            print(block)
            hashchain.add(block.blockhash())
            break

UTXO

import json
import requests

address = "3FkenCiXpSLqD8L79intRNXUgjRoH9sjXa"

res = requests.get("https://blockchain.info/unspent?active=" + address)
utxo_list = json.loads(res.text)["unspent_outputs"]

print(str(len(utxo_list)) + "個のutxoが見つかりました")
for utxo in utxo_list:
    print(utxo["tx_hash"] + ":" + str(utxo["value"]) + " satoshis")

blockcain exploreのtransactionからも確認できる。
https://www.blockchain.com/explorer

$ python3 utxo.py
2個のutxoが見つかりました
0aed6c01112af0a2dd51981e983ce41ef271c3bbec834121a47dbe31c30519d7:548546 satoshis
768b2e91eaa99208d32d3dde2a0f70214940584754b66378a33c9e3bd60136c3:311694 satoshis

HD Wallet

import os
import binascii
import ecdsa
import hmac
import hashlib

seed = os.urandom(32)
root_key = b'Bitcoin Seed'

def hmac_sha512(data, keymessage):
    hash = hmac.new(data, keymessage, hashlib.sha512).digest()
    return hash

def create_pubkey(private_key):
    publickey = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1).verifying_key.to_string()
    return publickey

master = hmac_sha512(seed, root_key)
master_secretkey = master[:32]
master_chaincode = master[32:]

master_publickey = create_pubkey(master_secretkey)
master_publickey_integer = int.from_bytes(master_publickey[32:], byteorder="big")

if master_publickey_integer % 2 == 0:
    master_publickey_x = b"\x02" + master_publickey[:32]
else:
    master_publickey_x = b"\x03" + master_publickey[:32]

print("マスター秘密鍵")
print(binascii.hexlify(master_secretkey))
print("\n")
print("マスターチェーンコード")
print(binascii.hexlify(master_chaincode))
print("\n")
print("マスター公開鍵")
print(binascii.hexlify(master_publickey_x))

index = 0
index_bytes = index.to_bytes(8, "big")
data = master_publickey_x + index_bytes
result_hmac512 = hmac_sha512(data, master_chaincode)

sum_integer = int.from_bytes(master_secretkey, "big") + int.from_bytes(result_hmac512[:32], "big")

p = 2**256 - 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 - 1
child_secretkey = (sum_integer % p).to_bytes(32, "big")

print("\n")
print("子秘密鍵")
print(binascii.hexlify(child_secretkey))

bitcoin アドレスの生成

import os
import ecdsa
import hashlib
import base58
from Crypto.Hash import RIPEMD160

private_key = os.urandom(32)
public_key = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1).verifying_key.to_string()

prefix_and_pubkey = b"\x04" + public_key

intermediate = hashlib.sha256(prefix_and_pubkey).digest()
ripemd160 = RIPEMD160.new()
ripemd160.update(intermediate)
hash160 = ripemd160.digest()

prefix_and_hash160 = b"\x00" + hash160

double_hash = hashlib.sha256(hashlib.sha256(prefix_and_hash160).digest()).digest()
checksum = double_hash[:4]
pre_address = prefix_and_hash160 + checksum

address = base58.b58encode(pre_address)
print(address.decode())

hashlibで以下のように書くとエラーになる。
ripemd160 = hashlib.new(‘ripemd160’)

$ python3 address.py
Traceback (most recent call last):
File “/usr/lib/python3.10/hashlib.py”, line 160, in __hash_new
return _hashlib.new(name, data, **kwargs)
ValueError: [digital envelope routines] unsupported

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File “/home/vagrant/dev/work/address.py”, line 12, in
ripemd160 = hashlib.new(‘ripemd160’)
File “/usr/lib/python3.10/hashlib.py”, line 166, in __hash_new
return __get_builtin_constructor(name)(data)
File “/usr/lib/python3.10/hashlib.py”, line 123, in __get_builtin_constructor
raise ValueError(‘unsupported hash type ‘ + name)
ValueError: unsupported hash type ripemd160

従って、pycryptodomeをインストールして、ripemd160を使う
$ pip install pycryptodome

bitcoin 秘密鍵の生成

import os
import binascii

private_key = os.urandom(32)
print(private_key)
print(binascii.hexlify(private_key))

32バイトの乱数を生成して、バイナリデータを16進数に変換

$ python3 wallet.py
b’W~\x9cj\xcc}\x0f1J\xab\xa6Hh\x87\xe7\xa6x2\xb1c,\xe9\x1dZp{R\xc3\x8e9-\xe2′
b’577e9c6acc7d0f314aaba6486887e7a67832b1632ce91d5a707b52c38e392de2′

公開鍵に楕円曲線暗号を使用します。
$ pip install ecdsa

import os
import ecdsa
import binascii

private_key = os.urandom(32)
public_key = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1).verifying_key.to_string()

print(binascii.hexlify(private_key))
print(binascii.hexlify(public_key))

y^2 = x^3 + 7 mod p

import os
import ecdsa
import binascii

private_key = os.urandom(32)
public_key = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1).verifying_key.to_string()

print(binascii.hexlify(private_key))
print(binascii.hexlify(public_key))

# public_key y-axis
public_key_y = int.from_bytes(public_key[32:], "big")

# create compressed public_key
if public_key_y % 2 == 0:
    public_key_compressed = b"\x02" + public_key[:32]
else:
    public_key_compressed = b"\x03" + public_key[:32]

print(binascii.hexlify(public_key_compressed))

python基礎

bit演算子

print(bin(10))
print(bin(13))
print(bin(10|13))
print(bin(10))
print(bin(13))
print(bin(10&13))

シフト演算

print(bin(15))
print(bin(15<<0))
print(bin(15<<1))
print(bin(15<<2))
print(bin(15<<3))
print(bin(15<<4))

print(bin(15))
print(bin(15>>0))
print(bin(15>>1))
print(bin(15>>2))
print(bin(15>>3))
print(bin(15>>4))

list

list2=[10,20,30]
list2.append(40)
print(list2)

list3=[1,2,3,4,5]
list3.insert(3, 10)
print(list3)

list4=[1,2,3,4,5]
print(len(list4))

dictionary

dic1 = {"A": 1, "B": 2}
dic2 = {}
dic2["key"] = "value"

print(dic1)
print(dic2)

class, instance

class Greet():
    def __init__(self, greet):
        self.value = greet

morning = Greet("おはよう!")
evening = Greet("こんばんわ!")

print(morning.value)
print(evening.value)

__str__(self)は自動的に文字出力する

class Person():
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return "Name:" + self.name + ", Age:" + str(self.age)

satoshi = Person("satoshi", 30)
print(satoshi)

import

import numpy as np

def area_circle(r):
    ans = np.pi * r **2
    return ans 
print(area_circle(3))

これをfromを使って以下のように書くことができる。コードを省略できるメリットがある。

from numpy import pi

def area_circle(r):
    ans = pi * r **2
    return ans 
print(area_circle(3))

sakura vpcのalma linuxでbitcoin coreを稼働したい

### sakura vpsの契約
1GB メモリ、100ssdで契約する。1ヶ月990円。
OSはalma linuxをインストール

### メモリ増強
1GBでは足りないらしいので、メモリを増強する
$ sudo fallocate -l 8G /swapfile
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
Setting up swapspace version 1, size = 8 GiB (8589930496 bytes)
no label, UUID=6a1c4a72-0340-40f3-be3a-9b7245afc3f0
$ sudo swapon /swapfile
$ free
total used free shared buff/cache available
Mem: 985552 126696 506488 31216 352368 689008
Swap: 8388604 0 8388604

### ポート解放
パケットフィルターでTCPの8333ポートを解放する

### alma linuxにbitcoin coreをinstall
$ sudo dnf install epel-release
$ sudo dnf upgrade
$ sudo dnf install snapd
$ sudo systemctl enable –now snapd.socket
$ sudo ln -s /var/lib/snapd/snap /snap
$ sudo snap install bitcoin-core
2023-10-31T08:08:07+09:00 INFO Waiting for automatic snapd restart…
bitcoin-core 25.1 from Bitcoin Core installed

### bit coin
$ bitcoin-core.cli –version
Bitcoin Core RPC client version v25.1.0
Copyright (C) 2009-2023 The Bitcoin Core developers

Please contribute if you find Bitcoin Core useful. Visit
for further information about the software.
The source code is available from .

This is experimental software.
Distributed under the MIT software license, see the accompanying file COPYING
or

$ snap info bitcoin-core
name: bitcoin-core
summary: Fully validating Bitcoin peer-to-peer network node, wallet and GUI
publisher: Bitcoin Core
store-url: https://snapcraft.io/bitcoin-core
contact: https://github.com/bitcoin-core/packaging/issues/new?title=snap:
license: unset
description: |
Bitcoin Core connects to the Bitcoin peer-to-peer network to download and
fully validate blocks and transactions. It also includes a wallet and
graphical user interface.
commands:
– bitcoin-core.cli
– bitcoin-core.daemon
– bitcoin-core.qt
– bitcoin-core.tx
– bitcoin-core.util
– bitcoin-core.wallet

$ bitcoin-core.cli -getinfo
error: timeout on transient error: Could not connect to the server 127.0.0.1:8332

Make sure the bitcoind server is running and that you are connecting to the correct RPC port.

### 再起動
VPS再起動

$ bitcoin-core.daemon -testnet

snapでinstallすると、実行コマンドが違うので注意が必要

shell scriptでメモリ使用率

#!/bin/bash

YMD=`date +"%Y/%m/%d %p %I:%M:%S"`

MEM_LOG="memory.log"

MEM_USED=`free | grep Mem | awk '{print($2-$3)/$2*100}'`
SWAP_USED=`free | grep Swap | awk '{print($3)/$2*100}'`

echo "$HOSTNAME memory is $MEM_USED %, date is $YMD" >> MEM_LOG
echo "$HOSTNAME swap is $SWAP_USED %, date is $YMD" >> MEM_LOG

vagrant memory is 86.4592 %, date is 2023/10/21 PM 07:25:24
vagrant swap is 4.532 %, date is 2023/10/21 PM 07:25:24

merkletree

こちらの記事を参考に動かしてみる
https://alis.to/gaxiiiiiiiiiiii/articles/3dy7vLZn0g89

トランザクションのハッシュ値作成の箇所は、Node(left.hash + right.hash)として、hash値を単純に横に繋げてそれをsha256でハッシュ化してるところが面白い。

from hashlib import sha256

class Node:
    def __init__(self, data):
        self.left     = None
        self.right    = None
        self.parent   = None
        self.sibling  = None
        self.position = None
        self.data     = data
        self.hash = sha256(data.encode()).hexdigest()
       
class Tree:
    def __init__(self, leaves):
        self.leaves = [Node(leaf) for leaf in leaves]
        self.layer  = self.leaves[::]
        self.root   = None
        self.build_tree()
    
    def build_layer(self):
        new_layer = []
        
        if len(self.layer) % 2 == 1:
            self.layer.append(self.layer[-1])
        
        for i in range(0, len(self.layer), 2):
            left = self.layer[i]
            right = self.layer[i+1]
            parent = Node(left.hash + right.hash)
            
            left.parent = parent
            left.sibling = right
            left.position = "left"
            
            right.parent = parent
            right.sibling = left
            right.position = "right"
            
            parent.left = left
            parent.right = right
            
            new_layer.append(parent)
        
        self.layer = new_layer
    
    def build_tree(self):
        while len(self.layer) > 1:
            self.build_layer()
        self.root = self.layer[0].hash
    
    def search(self, data):
        target = None
        hash_value = sha256(data.encode()).hexdigest()
        for node in self.leaves:
            if node.hash == hash_value:
                target = node
        return target
    
    def get_pass(self, data):
        target = self.search(data)
        markle_pass = []
        if not(target):
            return
        markle_pass.append(target.hash)
        while target.parent:
            sibling = target.sibling
            markle_pass.append((sibling.hash, sibling.position))
            target = target.parent
        return markle_pass   
      
def caluculator(markle_pass):
    value = markle_pass[0]
    for node in markle_pass[1:]:
        sib = node[0]
        position = node[1]
        if position == "right":
            value = sha256(value.encode() + sib.encode()).hexdigest()
        else:
            value = sha256(sib.encode() + value.encode()).hexdigest()
    return value