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