num = -20 print(abs(num))
num = -30.33 などにしても結果は同じ
随机应变 ABCD: Always Be Coding and … : хороший
num = -20 print(abs(num))
num = -30.33 などにしても結果は同じ
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が非常に重要
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
めちゃくちゃお洒落な書き方だな~
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を判定している
シンプルなログ出力はお馴染みの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で指定することでログ出力をカスタマイズできる。
a = [0, 1, 4, 9, 16, 25, 36, 49] print(a[:])
$ python3 test.py
[0, 1, 4, 9, 16, 25, 36, 49]
[:]の場合は始点から終点まで全てをスライスする。
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と同じであれば署名は有効
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を作り出すことができる
各コマンドがオプコードかスタックにプッシュされるオプコードかを判定する
1~77まではエレメントでそれ以上(78以上)がオプコードとみなす
class Script:
def __init__(self, cmds=None):
if cmds is None:
self.cmds = []
else:
self.cmds = cmds
// omission
@classmethod
def parse(cls, s):
length = read_varint(s)
cmds = []
count = 0
while count < length:
current = s.read(1)
count += 1
current_byte = current[0]
if current_byte >= 1 and current_byte <= 75:
n = current_byte
cmds.append(s.read(n))
count += n
elif current_byte == 76:
data_length = little_endian_to_int(s.read(1))
count += data_length + 1
elif current_byte == 77:
data_length = little_endian_to_int(s.read(2))
cmds.append(s.read(data_length))
count += data_length + 2
else:
op_code = current_byte
cmds.append(op_code)
if count != length:
raise SyntaxError('parsing script failed')
return cls(cmds)
### シリアライズ
整数の場合はオプコード、それ以外はエレメント
class Script:
//
def raw_serialize(self):
result = b''
for cmd in self.cmds:
if type(cmd) == int:
result += into_to_little_endian(cmd, 1)
else:
length = len(cmd)
if length < 75:
result += int_to_little_endian(length, 1)
elif length > 75 and length < 0x100:
result += int_to_little_endian(76, 1)
result += int_to_little_endian(length, 1)
elif length >= 0x100 and length <= 520:
result += int_to_little_endian(77, 1)
result += int_to_little_endian(length, 2)
else:
raise ValueError('too long an cmd')
result += cmd
return result
def serialize(self):
result = self.raw_serialize()
total = len(result)
return encode_varint(total) + result
### Script Fieldの連結
scriptオブジェクトは評価が必要なコマンドのセットを表す
スクリプを評価するにあたり、ロックボックスのScriptPubKey(outputsの2番目)とアンロックするScriptSig(inputsの3番目)のフィールドを連結する必要がある。
ScriptPubKeyはPrevious Transaction, ScriptSigはCurrent Transaction
ScriptSigがScriptPubKeyをunlockする
ScriptSigからのコマンドはScriptPubKeyのコマンドの上に配置し、処理するコマンドがなくなるまで1つづつ処理される
class Script:
def __add__(self, other):
return Script(self.cmds + other.cmds)
標準スクリプト(Base58, Bech32)
p2pk, p2pkh, p2sh, p2wpkh, p2wsh
ウォレットは様々なタイプを解釈する方法を知っており、適切なScriptPubKeyを作成する
ScriptPubKeyとScriptSigはどちらも同じ方法でパースされる
バイトが0x01から0x4b(10進数の75)の間の場合(nと呼ぶ)、次のnバイトを1つのエレメントとして読み取る。その範囲の値ではないときは、そのバイトはオペレーションを表している
0x00 – OP_0
0x51 – OP_1
0x60 – OP_16
0x76 – OP_DUP
0x93 – OP_ADD
0xa9 – OP_HASH160
0xac – OP_CHECHSIG
0x4bより長いエレメントの場合は、特別なオプコードOP_PUSHDATA1(76-255), OP_PUSHDATA2(256-520), OP_PUSHDATA4がある。