bitcoinのinputは前のトランザクションのoutputs。つまり、何か支払いを行うにはビットコインを受け取っている必要がある。
以下の二つが必要
– 前に受け取ったビットコインへの参照
– 支払う本人のビットコインであるという証明
インプットには署名を含む
インプットにはUTXOのインプットの数をトランザクションの中に含める
例えば、01000000(バージョン) 01(インプットの数) 813f…(インプット)
インプット数が255を上回るとシングルバイトで表す
varintとは、variable integerを短縮したもの。
2**64 – 1の範囲にある整数をエンコードする方法
– 253未満は1バイト
– 253 – 2**16 -1 の場合、253(fd)、その後リトルエンディアン2バイト
– 2**16 – 2**32 -1の場合、254(fe)、その後リトルエンディアン4バイト
– 2**32 – 2**64 -1の場合、255(ff)、その後リトルエンディアン8バイト
def read_varint(s): i = s.read(1)[0] if i == 0xfd: return little_endian_to_int(s.read(2)) elif i == 0xfe: return little_endian_to_int(s.read(4)) elif i == 0xff: return little_endian_to_int(s.read(8)) else: return i def encode_varint(i): if i < 0xfd: return bytes([i]) elif i < 0x10000: return b'\xfd' + int_to_little_endian(i, 2) elif i < 0x100000000: return b'\xfe' + int_to_little_endian(i, 4) elif i < 0x10000000000000000: return b'\xff' + int_to_little_endian(i, 8) else: raise ValueError('integer too large: {}'.format(i))
各インプットは4つのフィールドで構成されている。前のトランザクションとトランザクションアウトプットが支払われる方法(ScriptSig, シーケンス)
– 前のトランザクションID(32byte, little endian)
– 前のトランザクションインデックス(4byte)
– ScriptSig(鍵のついた箱を開けるもの、トランザクションアウトプット所有者、varint)
– シーケンス(Replace By Fee, OP_CHECKSEQUENCEVERIFY) ※当初はロックタイムフィールドと一緒に高頻度トレードに扱う予定だった => ライトニングネットワークの基盤になっているペイメントチャネル
class TxIn: def __init__(self, prev_tx, prev_index, script_sig=None, sequence=0xffffffff): self.prev_tx = prev_tx self.prev_index = prev_index if script_sig is None: self.script_sig = Script() else: self.script_sig = script_sig self.sequence = sequence def __repr__(self): return '{}:{}'.format( self.prev_tx.hex(), self.prev_index )
ノードはトランザクションが正しい箱の鍵を開け、存在しないビットコインを使用していないことを検証しないといけない
### スクリプトのparse
16進数のデータからScriptオブジェクトを取得
script.py
from io import BytesIO from logging import getLogger from unittest import TestCase from helper import ( encode_varint, int_to_little_endian, little_endian_to_int, read_varint ) from op import ( OP_CODE_FUNCTIONS, OP_CODE_NAMES ) class Script: def __init__(self, cmds=None): if cmds is None: self.cmds = [] else: self.cmds = cmds def __repr__(self): result = [] for cmd in self.cmds: if type(cmd) == int: if OP_CODE_NAMES.get(cmd): name = OP_CODE_NAMES.get(cmd) else: name = 'OP_[{}]'.format(cmd) result.append(name) else: result.append(cmd.hex()) return ' '.join(result) @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) 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) 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 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])) 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
op.py
import hashlib from Crypto.Hash import RIPEMD160 from helper import ( hash160, hash256 ) 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 def op_1negate(stack): stack.append(encode_num(-1)) return True def op_1(stack): stack.append(encode_num(1)) return True def op_2(stack): stack.append(encode_num(2)) return True def op_3(stack): stack.append(encode_num(3)) return True def op_4(stack): stack.append(encode_num(4)) return True def op_5(stack): stack.append(encode_num(5)) return True def op_6(stack): stack.append(encode_num(6)) return True def op_7(stack): stack.append(encode_num(7)) return True def op_8(stack): stack.append(encode_num(8)) return True def op_9(stack): stack.append(encode_num(9)) return True def op_10(stack): stack.append(encode_num(10)) return True def op_11(stack): stack.append(encode_num(11)) return True def op_12(stack): stack.append(encode_num(12)) return True def op_13(stack): stack.append(encode_num(13)) return True def op_14(stack): stack.append(encode_num(14)) return True def op_15(stack): stack.append(encode_num(15)) return True def op_16(stack): stack.append(encode_num(16)) return True def op_nop(stack): return True 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 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 def op_verify(stack): if len(stack) < 1: return False element = stack.pop() if decode_num(element) == 0: return False return True def op_return(stack): return False def op_toaltstack(stack, altstack): if len(stack) < 1: return False altstack.append(stack.pop()) return True def op_fromaltstack(stack, altstack): if len(altstack) < 1: return False stack.append(alstack.pop()) return True def op_2drop(stack): if len(stack) < 2: return False stack.pop() stack.pop() return True def op_2dup(stack): if len(stack) < 2: return False stack.extend(stack[-2:]) return True def op_3dup(stack): if len(stack) < 3: return False stack.extend(stack[-3:]) return True def op_2over(stack): if len(stack) < 4: return False stack.extend(stack[-4:-2]) return True def op_2rot(stack): if len(stack) < 6: return False stack.extend(stack[-6:-4]) return True def op_2swap(stack): if len(stack) < 4: return False stack[-4:] = stack[-2:] + stack[-4:-2] return True def op_ifdup(stack): if len(stack) < 1: return False if decode_num(stack[-1]) != 0: stack.append(stack[-1]) return True def op_depth(stack): stack.append(encode_num(len(stack))) return True def op_drop(stack): if len(stack) < 1: return False stack.pop() return True def op_dup(stack): if len(stack) < 1: return False stack.append(stack[-1]) return True def op_nip(stack): if len(stack) < 2: return False stack[-2:] = stack[-1:] return True def op_over(stack): if len(stack) < 2: return False stack.append(stack[-2]) return True def op_pick(stack): if len(stack) < 1: return False n = decode_num(stack.pop()) if len(stack) < n + 1: return False stack.append(stack[-n - 1]) return True def op_roll(stack): if len(stack) < 1: return False n = decode_num(stack.pop()) if len(stack) < n + 1: return False if n == 0: return True stack.append(stack.pop(-n - 1)) return True def op_rot(stack): if len(stack) < 3: return False stack.append(stack.pop(-3)) return True def op_swap(stack): if len(stack) < 2: return False stack.append(stack.pop(-2)) return True def op_tuck(stack): if len(stack) < 2: return False stack.insert(-2, stack[-1]) return True def op_size(stack): if len(stack) < 1: return False stack.append(encode_num(len(stack[-1]))) return True def op_equal(stack): if len(stack) < 2: return False element1 = stack.pop() element2 = stack.pop() if element1 == element2: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_equalverify(stack): return op_equal(stack) and op_verify(stack) def op_1add(stack): if len(stack) < 1: return False element = decode_num(stack.pop()) stack.append(encode_num(element + 1)) return True def op_1sub(stack): if len(stack) < 1: return False element = decode_num(stack.pop()) stack.append(encode_num(element - 1)) return True def op_negate(stack): if len(stack) < 1: return False element = decode_num(stack.pop()) stack.append(encode_num(-element)) return True def op_abs(stack): if len(stack) < 1: return False element = decode_num(stack.pop()) if element < 0: stack.append(encode_num(-element)) else: stack.append(encode_num(element)) return True def op_not(stack): if len(stack) < 1: return False element = stack.pop() if decode_num(element) == 0: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_0notequal(stack): if len(stack) < 1: return False element = stack.pop() if decode_num(element) == 0: stack.append(encode_num(0)) else: stack.append(encode_num(1)) return True def op_add(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) stack.append(encode_num(element1 + element2)) return True def op_sub(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) stack.append(encode_num(element2 - element1)) return True def op_booland(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element1 and element2: stack.append(encode_num(1)) else: stack.append(encode_num(2)) return True def op_boolor(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element1 or element2: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_numequal(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element1 == element2: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_numequalverify(stack): return op_numequal(stack) and op_verify(stack) def op_numnotequal(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element1 == element2: stack.append(encode_num(0)) else: stack.append(encode_num(1)) return True def op_lessthan(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element2 < element1: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_greaterthan(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element2 > element1: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_lessthanorequal(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element2 <= element1: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_greaterthanorequal(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element2 >= element1: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_min(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element1 < element2: stack.append(encode_num(element1)) else: stack.append(encode_num(element2)) return True def op_max(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element1 > element2: stack.append(encode_num(element1)) else: stack.append(encode_num(element2)) return True def op_within(stack): if len(stack) < 3: return False maximum = decode_num(stack.pop()) minimum = decode_num(stack.pop()) element = decode_num(stack.pop()) if element >= minimum and element < maximum: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_ripemd160(stack): if len(stack) < 1: return False element = stack.pop() # stack.append(hashlib.new('ripemd160', element).digest()) intermediate = hashlib.sha256(element).digest() ripemd160 = RIPEMD160.new() ripemd160.update(intermediate) stack.append(ripemd160.digest()) return True def op_sha1(stack): if len(stack) < 1: return False element = stack.pop() stack.append(hashlib.sha1(element).digest()) return True def op_sha256(stack): if len(stack) < 1: return False element = stack.pop() stack.append(hashlib.sha256(element).digest()) return True def op_hash160(stack): raise NotImplementedError def op_hash256(stack): if len(stack) < 1: return False element = stack.pop() stack.append(hash256(element)) return True def op_checksig(stack, z): raise NotImplementedError def op_checksigverify(stack, z): return op_checksig(stack, z) and op_verify(stack) def op_checkmultisig(stack, z): raise NotImplementedError def op_checkmultisigverify(stack, z): return op_checkmultisig(stack, z) and op_verify(stack) def op_checklocktimeverify(stack, locktime, sequence): if sequence == 0xffffffff: return False if len(stack) < 1: return False element = decode_num(stack[-1]) if element < 0: return False if element < 500000000 and locktime > 500000000: return False if locktime < element: return False return True def op_checksequenceverify(stack, version, sequence): if sequence & (1 << 31) == (1 << 31): return False if len(stack) < 1: return False element = decode_num(stack[-1]) if element < 0: return False if element & (1 << 31) == (1 << 31): if version < 2: return False elif sequence & (1 << 31) == (1 << 31): return False elif element & (1 << 22) != sequence & (1 << 22): return False elif element & 0xffff > sequence & 0xffff: return False return True OP_CODE_FUNCTIONS = { 0: op_0, 79: op_1negate, 81: op_1, 82: op_2, 83: op_3, 84: op_4, 85: op_5, 86: op_6, 87: op_7, 88: op_8, 89: op_9, 90: op_10, 91: op_11, 92: op_12, 93: op_13, 94: op_14, 95: op_15, 96: op_16, 97: op_nop, 99: op_if, 100: op_notif, 105: op_verify, 106: op_return, 107: op_toaltstack, 108: op_fromaltstack, 109: op_2drop, 110: op_2dup, 111: op_3dup, 112: op_2over, 113: op_2rot, 114: op_2swap, 115: op_ifdup, 116: op_depth, 117: op_drop, 118: op_dup, 119: op_nip, 120: op_over, 121: op_pick, 122: op_roll, 123: op_rot, 124: op_swap, 125: op_tuck, 130: op_size, 135: op_equal, 136: op_equalverify, 139: op_1add, 140: op_1sub, 143: op_negate, 144: op_abs, 145: op_not, 146: op_0notequal, 147: op_add, 148: op_sub, 154: op_booland, 155: op_boolor, 156: op_numequal, 157: op_numequalverify, 158: op_numnotequal, 159: op_lessthan, 160: op_greaterthan, 161: op_lessthanorequal, 162: op_greaterthanorequal, 163: op_min, 164: op_max, 165: op_within, 166: op_ripemd160, 167: op_sha1, 168: op_sha256, 169: op_hash160, 170: op_hash256, 172: op_checksig, 173: op_checksigverify, 174: op_checkmultisig, 175: op_checkmultisigverify, 176: op_nop, 177: op_checklocktimeverify, 178: op_checksequenceverify, 179: op_nop, 180: op_nop, 181: op_nop, 182: op_nop, 183: op_nop, 184: op_nop, 185: op_nop, } OP_CODE_NAMES = { 0: 'OP_0', 76: 'OP_PUSHDATA1', 77: 'OP_PUSHDATA2', 78: 'OP_PUSHDATA4', 79: 'OP_1NEGATE', 81: 'OP_1', 82: 'OP_2', 83: 'OP_3', 84: 'OP_4', 85: 'OP_5', 86: 'OP_6', 87: 'OP_7', 88: 'OP_8', 89: 'OP_9', 90: 'OP_10', 91: 'OP_11', 92: 'OP_12', 93: 'OP_13', 94: 'OP_14', 95: 'OP_15', 96: 'OP_16', 97: 'OP_NOP', 99: 'OP_IF', 100: 'OP_NOTIF', 103: 'OP_ELSE', 104: 'OP_ENDIF', 105: 'OP_VERIFY', 106: 'OP_RETURN', 107: 'OP_TOALTSTACK', 108: 'OP_FROMALTSTACK', 109: 'OP_2DROP', 110: 'OP_2DUP', 111: 'OP_3DUP', 112: 'OP_2OVER', 113: 'OP_2ROT', 114: 'OP_2SWAP', 115: 'OP_IFDUP', 116: 'OP_DEPTH', 117: 'OP_DROP', 118: 'OP_DUP', 119: 'OP_NIP', 120: 'OP_OVER', 121: 'OP_PICK', 122: 'OP_ROLL', 123: 'OP_ROT', 124: 'OP_SWAP', 125: 'OP_TUCK', 130: 'OP_SIZE', 135: 'OP_EQUAL', 136: 'OP_EQUALVERIFY', 139: 'OP_1ADD', 140: 'OP_1SUB', 143: 'OP_NEGATE', 144: 'OP_ABS', 145: 'OP_NOT', 146: 'OP_0NOTEQUAL', 147: 'OP_ADD', 148: 'OP_SUB', 154: 'OP_BOOLAND', 155: 'OP_BOOLOR', 156: 'OP_NUMEQUAL', 157: 'OP_NUMEQUALVERIFY', 158: 'OP_NUMNOTEQUAL', 159: 'OP_LESSTHAN', 160: 'OP_GREATERTHAN', 161: 'OP_LESSTHANOREQUAL', 162: 'OP_GREATERTHANOREQUAL', 163: 'OP_MIN', 164: 'OP_MAX', 165: 'OP_WITHIN', 166: 'OP_RIPEMD160', 167: 'OP_SHA1', 168: 'OP_SHA256', 169: 'OP_HASH160', 170: 'OP_HASH256', 171: 'OP_CODESEPARATOR', 172: 'OP_CHECKSIG', 173: 'OP_CHECKSIGVERIFY', 174: 'OP_CHECKMULTISIG', 175: 'OP_CHECKMULTISIGVERIFY', 176: 'OP_NOP1', 177: 'OP_CHECKLOCKTIMEVERIFY', 178: 'OP_CHECKSEQUENCEVERIFY', 179: 'OP_NOP4', 180: 'OP_NOP5', 181: 'OP_NOP6', 182: 'OP_NOP7', 183: 'OP_NOP8', 184: 'OP_NOP9', 185: 'OP_NOP10', }
main.py
from io import BytesIO from script import Script script_hex = ('6b483045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed01210349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278a') stream = BytesIO(bytes.fromhex(script_hex)) script_sig = Script.parse(stream) print(script_sig)
$ python3 main.py
3045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed01 0349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278a