electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 1b19cdd0f44ed5b65b30631f21db769fe5aaeefb
parent 0137626a63d0da8fffb186d11dccd203c86243bb
Author: SomberNight <somber.night@protonmail.com>
Date:   Tue, 28 Aug 2018 20:17:21 +0200

transaction.py: fix script_GetOp for malformed scripts

related fyookball/electrum#829

Diffstat:
Melectrum/tests/test_transaction.py | 6++++++
Melectrum/transaction.py | 39+++++++++++++++++++++------------------
2 files changed, 27 insertions(+), 18 deletions(-)

diff --git a/electrum/tests/test_transaction.py b/electrum/tests/test_transaction.py @@ -307,6 +307,12 @@ class TestTransaction(SequentialTestCase): txid = '9b9f39e314662a7433aadaa5c94a2f1e24c7e7bf55fc9e1f83abd72be933eb95' self._run_naive_tests_on_tx(raw_tx, txid) + # see https://bitcoin.stackexchange.com/questions/38006/txout-script-criteria-scriptpubkey-critieria + def test_txid_invalid_op_return(self): + raw_tx = '01000000019ac03d5ae6a875d970128ef9086cef276a1919684a6988023cc7254691d97e6d010000006b4830450221009d41dc793ba24e65f571473d40b299b6459087cea1509f0d381740b1ac863cb6022039c425906fcaf51b2b84d8092569fb3213de43abaff2180e2a799d4fcb4dd0aa012102d5ede09a8ae667d0f855ef90325e27f6ce35bbe60a1e6e87af7f5b3c652140fdffffffff080100000000000000010101000000000000000202010100000000000000014c0100000000000000034c02010100000000000000014d0100000000000000044dffff010100000000000000014e0100000000000000064effffffff0100000000' + txid = 'ebc9fa1196a59e192352d76c0f6e73167046b9d37b8302b6bb6968dfd279b767' + self._run_naive_tests_on_tx(raw_tx, txid) + # these transactions are from Bitcoin Core unit tests ---> # https://github.com/bitcoin/bitcoin/blob/11376b5583a283772c82f6d32d0007cdbf5b8ef0/src/test/data/tx_valid.json diff --git a/electrum/transaction.py b/electrum/transaction.py @@ -59,6 +59,10 @@ class NotRecognizedRedeemScript(Exception): pass +class MalformedBitcoinScript(Exception): + pass + + TxOutput = NamedTuple("TxOutput", [('type', int), ('address', str), ('value', Union[int, str])]) # ^ value is str when the output is set to max: '!' @@ -263,13 +267,16 @@ def script_GetOp(_bytes : bytes): if opcode <= opcodes.OP_PUSHDATA4: nSize = opcode if opcode == opcodes.OP_PUSHDATA1: - nSize = _bytes[i] + try: nSize = _bytes[i] + except IndexError: raise MalformedBitcoinScript() i += 1 elif opcode == opcodes.OP_PUSHDATA2: - (nSize,) = struct.unpack_from('<H', _bytes, i) + try: (nSize,) = struct.unpack_from('<H', _bytes, i) + except struct.error: raise MalformedBitcoinScript() i += 2 elif opcode == opcodes.OP_PUSHDATA4: - (nSize,) = struct.unpack_from('<I', _bytes, i) + try: (nSize,) = struct.unpack_from('<I', _bytes, i) + except struct.error: raise MalformedBitcoinScript() i += 4 vch = _bytes[i:i + nSize] i += nSize @@ -281,21 +288,11 @@ def script_GetOpName(opcode): return (opcodes.whatis(opcode)).replace("OP_", "") -def decode_script(bytes): - result = '' - for (opcode, vch, i) in script_GetOp(bytes): - if len(result) > 0: result += " " - if opcode <= opcodes.OP_PUSHDATA4: - result += "%d:"%(opcode,) - result += short_hex(vch) - else: - result += script_GetOpName(opcode) - return result - - def match_decoded(decoded, to_match): + if decoded is None: + return False if len(decoded) != len(to_match): - return False; + return False for i in range(len(decoded)): if to_match[i] == opcodes.OP_PUSHDATA4 and decoded[i][0] <= opcodes.OP_PUSHDATA4 and decoded[i][0]>0: continue # Opcodes below OP_PUSHDATA4 all just push data onto stack, and are equivalent. @@ -413,7 +410,10 @@ def parse_scriptSig(d, _bytes): def parse_redeemScript_multisig(redeem_script: bytes): - dec2 = [ x for x in script_GetOp(redeem_script) ] + try: + dec2 = [ x for x in script_GetOp(redeem_script) ] + except MalformedBitcoinScript: + raise NotRecognizedRedeemScript() try: m = dec2[0][0] - opcodes.OP_1 + 1 n = dec2[-2][0] - opcodes.OP_1 + 1 @@ -434,7 +434,10 @@ def parse_redeemScript_multisig(redeem_script: bytes): def get_address_from_output_script(_bytes, *, net=None): - decoded = [x for x in script_GetOp(_bytes)] + try: + decoded = [x for x in script_GetOp(_bytes)] + except MalformedBitcoinScript: + decoded = None # The Genesis Block, self-payments, and pay-by-IP-address payments look like: # 65 BYTES:... CHECKSIG