diff --git a/riemann/script/serialization.py b/riemann/script/serialization.py index 4265268..fc72560 100644 --- a/riemann/script/serialization.py +++ b/riemann/script/serialization.py @@ -1,4 +1,5 @@ import riemann +from .. import utils from .opcodes import CODE_TO_INT, INT_TO_CODE @@ -10,29 +11,40 @@ def serialize(script_string): serialized_script = bytearray() for token in string_tokens: - if token == 'OP_CODESEPARATOR': - raise NotImplementedError('OP_CODESEPARATOR is a bad idea.') - try: + if token == 'OP_CODESEPARATOR' or token == 'OP_PUSHDATA4': + raise NotImplementedError('{} is a bad idea.'.format(token)) + + if token in riemann.network.CODE_TO_INT_OVERWRITE: serialized_script.extend( [riemann.network.CODE_TO_INT_OVERWRITE[token]]) - continue # Skip rest of loop - except (AttributeError, KeyError): - pass - try: - serialized_script.extend([CODE_TO_INT[token]]) # Put it in there - continue # Skip rest of loop - except KeyError: - pass - token_bytes = bytes.fromhex(token) + elif token in CODE_TO_INT: + serialized_script.extend([CODE_TO_INT[token]]) - if len(token_bytes) > 75: - # TODO - raise NotImplementedError('OP_PUSHDATA1-4 not supported yet.') + else: + token_bytes = bytes.fromhex(token) + + if len(token_bytes) <= 75: + op = 'OP_PUSH_{}'.format(len(token_bytes)) + serialized_script.extend([CODE_TO_INT[op]]) + serialized_script.extend(token_bytes) + + elif len(token_bytes) > 75 and len(token_bytes) <= 255: + op = 'OP_PUSHDATA1' + serialized_script.extend([CODE_TO_INT[op]]) + serialized_script.extend(utils.i2le(len(token_bytes))) + serialized_script.extend(token_bytes) + + elif len(token_bytes) > 255 and len(token_bytes) <= 1000: + op = 'OP_PUSHDATA2' + serialized_script.extend([CODE_TO_INT[op]]) + serialized_script.extend( + utils.i2le_padded(len(token_bytes), 2)) + serialized_script.extend(token_bytes) - op = 'OP_PUSH_{}'.format(len(token_bytes)) - serialized_script.extend([CODE_TO_INT[op]]) - serialized_script.extend(token_bytes) + else: + raise NotImplementedError( + 'Hex string too long to serialize.') return serialized_script @@ -65,6 +77,27 @@ def deserialize(serialized_script): 'Push {} caused out of bounds exception.' .format(current_byte)) + elif current_byte == 76: + # next hex blob length + blob_len = serialized_script[i + 1] + + deserialized.append( + serialized_script[i + 2: i + 2 + blob_len].hex()) + + i += 2 + blob_len + + elif current_byte == 77: + # next hex blob length + blob_len = utils.le2i(serialized_script[i + 1: i + 3]) + + deserialized.append( + serialized_script[i + 3: i + 3 + blob_len].hex()) + + i += 3 + blob_len + + elif current_byte == 78: + raise NotImplementedError('OP_PUSHDATA4 is a bad idea.') + else: if current_byte in riemann.network.INT_TO_CODE_OVERWRITE: deserialized.append( @@ -82,6 +115,6 @@ def deserialize(serialized_script): def hex_deserialize(script_hex): ''' - bytearray -> hex_str + hex_str -> str ''' return deserialize(bytes.fromhex(script_hex)) diff --git a/riemann/tests/helpers.py b/riemann/tests/helpers.py index d9303fa..6ea4f91 100644 --- a/riemann/tests/helpers.py +++ b/riemann/tests/helpers.py @@ -213,3 +213,13 @@ DCR_2_EXPECTED_SIGHASH_ALL_ANYONECANPAY = bytes.fromhex('c75779c947b3c0e8828db370c8d5597c6dd91a48e287d1bfca705637943d200e') DCR_2_EXPECTED_SIGHASH_SINGLE = bytes.fromhex('a1f4f2ced71352153ffee5dd570da5d609ecd5ce04e1db808c238554d758fb13') DCR_2_EXPECTED_SIGHASH_SINGLE_ANYONECANPAY = bytes.fromhex('1b83a4d2a1a70204587491f7f6e110704e98e1b8da04219efba5dde14eaccf1f') + + + +# P2SH OP_PUSHDATA1 +P2SH_PUSHDATA1_SCRIPT = 'OP_0 3044022024bb241b26586a4c614ba38fec83a50904d5daeed0975e25eae095e5e911989e022073d99364454fc572a189a2dcf11c6b182a45c5177e746b731448abe3d9e4fe5001 30440220319dbd5a69bcaa73e569c5e068edb03f6c52344cd9068d248925256463608c8f02201b4f35ee176d85395aa1eb49aa80adc22cad820d26d62cf462889b791b98aaf801 522102975ddf75126ef880d1b56ea194141ea0ceb2d9e12298b74d54432cbd835358542103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f8802103e5dc75b59e4c67bfea266314d0b1da1e317f5b7d1e4cf1975442b79e542de74853ae' +P2SH_PUSHDATA1_SERIALIZED = '00473044022024bb241b26586a4c614ba38fec83a50904d5daeed0975e25eae095e5e911989e022073d99364454fc572a189a2dcf11c6b182a45c5177e746b731448abe3d9e4fe50014730440220319dbd5a69bcaa73e569c5e068edb03f6c52344cd9068d248925256463608c8f02201b4f35ee176d85395aa1eb49aa80adc22cad820d26d62cf462889b791b98aaf8014c69522102975ddf75126ef880d1b56ea194141ea0ceb2d9e12298b74d54432cbd835358542103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f8802103e5dc75b59e4c67bfea266314d0b1da1e317f5b7d1e4cf1975442b79e542de74853ae' + +# P2SH OP_PUSHDATA2 +P2SH_PUSHDATA2_SCRIPT = 'OP_0 3044022024bb241b26586a4c614ba38fec83a50904d5daeed0975e25eae095e5e911989e022073d99364454fc572a189a2dcf11c6b182a45c5177e746b731448abe3d9e4fe5001 30440220319dbd5a69bcaa73e569c5e068edb03f6c52344cd9068d248925256463608c8f02201b4f35ee176d85395aa1eb49aa80adc22cad820d26d62cf462889b791b98aaf801 522102975ddf75126ef880d1b56ea194141ea0ceb2d9e12298b74d54432cbd835358542103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f8802103e5dc75b59e4c67bfea266314d0b1da1e317f5b7d1e4cf1975442b79e542de74853ae2102975ddf75126ef880d1b56ea194141ea0ceb2d9e12298b74d54432cbd835358542103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f8802103e5dc75b59e4c67bfea266314d0b1da1e317f5b7d1e4cf1975442b79e542de74853ae2102975ddf75126ef880d1b56ea194141ea0ceb2d9e12298b74d54432cbd835358542103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f8802103e5dc75b59e4c67bfea266314d0b1da1e317f5b7d1e4cf1975442b79e542de74853ae' +P2SH_PUSHDATA2_SERIALIZED = '00473044022024bb241b26586a4c614ba38fec83a50904d5daeed0975e25eae095e5e911989e022073d99364454fc572a189a2dcf11c6b182a45c5177e746b731448abe3d9e4fe50014730440220319dbd5a69bcaa73e569c5e068edb03f6c52344cd9068d248925256463608c8f02201b4f35ee176d85395aa1eb49aa80adc22cad820d26d62cf462889b791b98aaf8014d3901522102975ddf75126ef880d1b56ea194141ea0ceb2d9e12298b74d54432cbd835358542103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f8802103e5dc75b59e4c67bfea266314d0b1da1e317f5b7d1e4cf1975442b79e542de74853ae2102975ddf75126ef880d1b56ea194141ea0ceb2d9e12298b74d54432cbd835358542103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f8802103e5dc75b59e4c67bfea266314d0b1da1e317f5b7d1e4cf1975442b79e542de74853ae2102975ddf75126ef880d1b56ea194141ea0ceb2d9e12298b74d54432cbd835358542103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f8802103e5dc75b59e4c67bfea266314d0b1da1e317f5b7d1e4cf1975442b79e542de74853ae' diff --git a/riemann/tests/script/test_serialization.py b/riemann/tests/script/test_serialization.py index 626646d..954d1dc 100644 --- a/riemann/tests/script/test_serialization.py +++ b/riemann/tests/script/test_serialization.py @@ -19,9 +19,15 @@ def test_serialize(self): def test_serialize_error(self): with self.assertRaises(NotImplementedError) as context: - ser.serialize('00' * 77) + ser.serialize('00' * 65999) self.assertIn( - 'OP_PUSHDATA1-4 not supported yet.', + 'Hex string too long to serialize.', + str(context.exception)) + + with self.assertRaises(NotImplementedError) as context: + ser.serialize('OP_PUSHDATA4') + self.assertIn( + 'OP_PUSHDATA4 is a bad idea.', str(context.exception)) with self.assertRaises(NotImplementedError) as context: @@ -45,6 +51,26 @@ def test_hex_serialize(self): ser.hex_serialize('OP_IF'), bytes([99]).hex()) + def test_hex_serialize_OP_PUSHDATA1(self): + self.assertEqual( + ser.hex_serialize(helpers.P2SH_PUSHDATA1_SCRIPT), + helpers.P2SH_PUSHDATA1_SERIALIZED) + + def test_hex_deserialize_OP_PUSHDATA1(self): + self.assertEqual( + ser.hex_deserialize(helpers.P2SH_PUSHDATA1_SERIALIZED), + helpers.P2SH_PUSHDATA1_SCRIPT) + + def test_hex_serialize_OP_PUSHDATA2(self): + self.assertEqual( + ser.hex_serialize(helpers.P2SH_PUSHDATA2_SCRIPT), + helpers.P2SH_PUSHDATA2_SERIALIZED) + + def test_hex_deserialize_OP_PUSHDATA2(self): + self.assertEqual( + ser.hex_deserialize(helpers.P2SH_PUSHDATA2_SERIALIZED), + helpers.P2SH_PUSHDATA2_SCRIPT) + def test_deserialize(self): self.assertEqual( helpers.MSIG_TWO_TWO_SCRIPT,