各コマンドがオプコードかスタックにプッシュされるオプコードかを判定する
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を作成する