def encode_num(num):
if num == 0:
return b''
abs_num = abs(num)
negative = num < 0
result = bytearray()
while abs_num:
result.append(abs_num & 0xff)
abs_num >>= 8
if result[-1] & 0x80:
if negative:
result.append(0x80)
else:
result.append(0)
elif negative:
result[-1] != 0x80
return bytes(result)
def decode_num(element):
if element == b'':
return 0
big_endian = element[::-1]
if big_endian[0] & 0x80:
negative = True
result = big_endian[0] & 0x7f
else:
negative = False
result = big_endian[0]
for c in big_endian[1:]:
result <<= 8
result += c
if negative:
return -result
else:
return result
def op_0(stack):
stack.append(encode_num(0))
return True
Python bytearray()
bytearray() method returns a bytearray object which is an array of given bytes.
prime = [2, 3, 5, 7] print(bytearray(prime))
$ python3 test.py
bytearray(b’\x02\x03\x05\x07′)
0 <= x < 256 の整数
Python abs() is absolute value
num = -20 print(abs(num))
num = -30.33 などにしても結果は同じ
scriptのchecksigによる評価
evaluateにより、script_sigとscript_pubkeyの検証を行いたい
Scriptの第二引数である0xac(172)はOP_CHECKSIG
from script import Script
z = 0x7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d
sec = bytes.fromhex('04887387e452b8eacc4acfde10d9aaf7f6d9a0f975aabb10d006e\
4da568744d06c61de6d95231cd89026e286df3b6ae4a894a3378e393e93a0f45b666329a0ae34')
sig = bytes.fromhex('3045022000eff69ef2b1bd93a66ed5219add4fb51e11a840f4048\
76325a1e8ffe0529a2c022100c7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fd\
dbdce6feab601')
script_pubkey = Script([sec, 0xac])
script_sig = Script([sig])
combined_script = script_sig + script_pubkey
print(combined_script.evaluate(z))
def evaluate(self, z):
cmds = self.cmds[:]
stack = []
altstack = []
while len(cmds) > 0:
cmd = cmds.pop(0)
if type(cmd) == int:
operation = OP_CODE_FUNCTIONS[cmd]
if cmd in (99, 100):
if not operation(stack, cmds):
LOGGER.info('bad op: {}'.format(OP_CODE_NAMES[cmd]))
return False
elif cmd in (107, 108):
if not operation(stack, altstack):
LOGGER.info('bad op: {}'.format(OP_CODE_NAMES[cmd]))
return False
elif cmd in (172, 173, 174, 175):
if not operation(stack, z):
LOGGER.info('bad op: {}'.format(OP_CODE_NAMES[cmd]))
return False
else:
if not operation(stack):
LOGGER.info('bad op: {}'.format(OP_CODE_NAMES[cmd]))
return False
else:
stack.append(cmd)
if len(stack) == 0:
return False
if statck.pop() == b'':
return False
return True
99: op_if,
100: op_notif,
op_if: If the top stack value is not False, the statements are executed. The top stack value is removed.
def op_if(stack, items):
if len(stack) < 1:
return False
true_items = []
false_itmes = []
current_array = true_items
found = False
num_endifs_needed = 1
while len(items) > 0:
item = items.pop(0)
if item in (99, 100):
num_endifs_needed += 1
current_array.append(item)
elif num_endifs_needed == 1 and item == 103:
current_array = false_items
elif item == 104:
if num_endifs_needed == 1:
found = True
break
else:
num_endifs_neede -= 1
current_array.append(item)
else:
current_array.append(item)
if not found:
return False
element = stack.pop()
if decode_num(element) == 0:
items[:0] = false_items
else:
items[:0] = true_items
return True
op_notif: If the top stack value is False, the statements are executed. The top stack value is removed.
def op_notif(stack, items):
if len(stack) < 1:
return False
true_items = []
false_items = []
current_array = true_items
found = False
num_endifs_needed = 1
while len(items) > 0:
item = items.pop(0)
if item in (99, 100):
num_endifs_needed += 1
current_array.append(item)
elif num_endifs_needed == 1 and item == 103:
current_array = false_items
elif item == 104:
if num_endifs_needed == 1:
found = True
break
else:
num_endifs_needed -= 1
current_array.append(item)
else:
current_array.append(item)
if not found:
return False
element = stack.pop()
if decode_num(element) == 0:
items[:0] = true_items
else:
items[:0] = false_items
return True
—–
107: op_toaltstack,
108: op_fromaltstack,
op_toaltstack:
def op_toaltstack(stack, altstack):
if len(stack) < 1:
return False
altstack.append(stack.pop())
return True
op_fromaltstack
def op_fromaltstack(stack, altstack):
if len(altstack) < 1:
return False
stack.append(alstack.pop())
return True
172: op_checksig,
173: op_checksigverify,
174: op_checkmultisig,
175: op_checkmultisigverify,
0xac(172)のOP_CHECKSIGが非常に重要
Python 関数を数字(数字コード)で実行する方法
def OP_NOP():
print("This is OP_NOP, 76")
def OP_IF():
print("This is OP_IF, 99")
def OP_NOTIF():
print("This is OP_NOTIF, 100")
OP_CODE = {
76: OP_NOP,
99: OP_IF,
100: OP_NOTIF
}
func = OP_CODE[76]
func()
$ python3 test.py
This is OP_NOP, 76
bitcoinのOPcode一覧はこちら
https://en.bitcoin.it/wiki/Script
めちゃくちゃお洒落な書き方だな~
Python if not condition
a = True
if not a:
print('a is false')
in String
a = ''
if not a:
print('a is empty')
def mul(num):
if type(num) == int:
print(num * 2)
return True
else:
return False
n = 'a'
if not mul(n):
print('please input number')
if notは基本的にTrue or Falseを判定している
PythonのLogging
シンプルなログ出力はお馴染みのprint文
print("I am the simplest log.")
ファイル出力、メール送信、HTTP通信など動作ログをよりフレキシブルに出力したい場合にはloggingモジュールを使用してログを出力する。
### ログレベル
DEBUG, INFO, WARNING, ERROR, CRITICAL
出力
import logging
logging.info("info log")
logging.warning("warning log")
$ python3 test.py
WARNING:root:warning log
info はコマンドラインに出力されない
loggerオブジェクトを取得してログを出力する
import logging
logger = logging.getLogger("sample")
logger.info("info log")
logger.warning("warning log")
$ python3 test.py
warning log
import logging
logger = logging.getLogger("sample")
logger.setLevel(logging.DEBUG)
logger.info("info log")
logger.warning("warning log")
### ハンドラ
StreamHandler, FileHandler, SMTPHandler, HTTPHandlerがある
import logging
logger = logging.getLogger("sample")
logger.setLevel(logging.DEBUG)
st_handler = logging.StreamHandler()
logger.addHandler(st_handler)
logger.info("info log")
logger.warning("warning log")
handlerで指定することでログ出力をカスタマイズできる。
Pythonのスライス[:]
a = [0, 1, 4, 9, 16, 25, 36, 49] print(a[:])
$ python3 test.py
[0, 1, 4, 9, 16, 25, 36, 49]
[:]の場合は始点から終点まで全てをスライスする。
ECDSAの原理
eは秘密鍵、pは公開鍵
eG = P
ランダムな256ビットの数字k
k * G = R (x座標をr)
uG + vP = kG
kはランダムに選ばれる数字で、u, vは署名者が選ぶ
vP = (k – u)G
P = ((k-u)/v)G
eG = ((k-u)/v)G のため、 e = (k – v) / u
つまり、秘密鍵 e = (k – v) / u を満たす(u, v)の組み合わせであればどれでも良い
eは秘密鍵を知っている人のみわかる
目的は署名ハッシュと呼ぶ。署名ハッシュはzで表す。
uG + vPに当てはめると、 u = z/s, v = z/s
uG + vP = R(目的) = kG
uG + veP = kG
u + ve = k
z/s + re/s = k
(z + re)/s = k
s = (z + re)/k
k は256ビットを保証するためhash256を2回繰り返す
1. 署名(r, s)、署名対象のハッシュをz, 公開鍵をPとする
2. u = z/s, v = r/sを計算する
3. uG + vP = Rを計算する
4. Rのx座標がrと同じであれば署名は有効
標準Script p2pk(pay to public key)
ScriptPubKeyの形式がp2pkであるトランザクションアウトプット
ECDSAの署名を検証するには、メッセージz, 公開鍵P, 署名r, sが必要
p2pkでは、ビットコインは公開鍵に送られ、秘密鍵の所有者は署名を作成することによりビットコインをアンロックできる。
ScriptPubKeyはビットコインを秘密鍵の所有者の管理下に置く。
P は公開鍵(x,y) 256bit
e は秘密鍵
e.g.
ScriptPubKey
41 – length of pubkey
0411…a3 pubkey
ac – OP_CHECKSIG
ScriptSig
47 – length of signature
3044 .. 01 – signature
OP_CHECKSIG, pubkey + signature のコマンドセットにする
スタックの先頭エレメントがゼロ以外のときは、有効なscript sigとみなされる
stackにpubkey, signatureをpushし、OP_CHECKSIGで有効であればstackに1をpush, 有効でなければ0をpushする
0以外であれば処理を終了する
0の場合は、スクリプトは無効となり、トランザクション自体が無効になる
秘密鍵を知っている人だけが有効なScriptSigを作り出すことができる