コインをロック/アンロックする機能はビットコインを転送する仕組み(スマートコントラクト)
ロック: ビットコインにエンティティを与える
アンロック: 受け取ったビットコインを支払う
楕円曲線暗号はScriptがトランザクションに適切に使用許可が与えられているか検証する
Scriptはビットコインのスマートコントラクト言語、使用可能な条件を表すためのプログラミング言語
ScriptはForthに似たスタックベース言語、ループ処理できない
L Ethereumはチューリング完全だが、プログラム実行時にgasを支払わなければならない
### Scriptの動作
スタック上の要素を操作する
コマンドはエレメント(データ)とオペレーション(何らかの操作)
エレメントe.g. signature, pubkey
オペレーションe.g. OP_DUB スタックの先頭エレメントを複製してスタックにプッシュ
OP_CHECKSIG: pubkey, signatureをプップし、有効かどうかを確認する
def op_dup(stack): if len(stack) < 1: return False stack.append(stack[-1]) return True 118: op_dup,
def op_hash160(stack): if len(stack) < 1: return False element = stack.pop() h160 = hash160(element) stack.append(h160) return True
### スクリプトのパースとシリアライズ
def __add__(self, other): return Script(self.cmds + other.cmds) @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)) cmds.append(s.read(data_length)) 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) def raw_serialize(self): result = b'' for cmd in self.cmds: if type(cmd) == int: result += int_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
ビットコインには様々な種類の標準的なスクリプトがある
p2pk, p2pkh, p2sh, p2wpkh, p2wsh
### p2pk
pubkey, OP_CHECHSIG, signature
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))
op_checksig
def op_checksig(stack, z): if len(stack) < 2: return False sec_pubkey = stack.pop() der_signature = stack.pop()[:-1] try: point = S256Point.parse(sec_pubkey) sig = Signature.parse(der_signature) except (ValueError, SyntaxError) as e: return False if point.verify(z, sig): stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True
from script import Script script_pubkey = Script([0x76, 0x76, 0x95, 0x93, 0x56, 0x87]) script_sig = Script([0x52]) combined_script = script_sig + script_pubkey print(combined_script.evaluate(0))
from script import Script script_pubkey = Script([0x6e, 0x87, 0x91, 0x69, 0xa7, 0x7c, 0xa7, 0x87]) c1 = '255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f576964746820\ 32203020522f4865696768742033203020522f547970652034203020522f537562747970652035\ 203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e67\ 74682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8\ fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1ff\ fe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db169\ 0901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc\ 942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a215664613097\ 89606bd0bf3f98cda8044629a1' c2 = '255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f576964746820\ 32203020522f4865696768742033203020522f547970652034203020522f537562747970652035\ 203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e67\ 74682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8\ fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1ff\ fe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a9\ 0901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0\ a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd3097\ 91d06bd0af3f98cda4bc4629b1' collision1 = bytes.fromhex(c1) collision2 = bytes.fromhex(c2) script_sig = Script([collision1, collision2]) combined_script = script_sig + script_pubkey print(combined_script.evaluate(0))