Scriptのパースとシリアライズ

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