diff --git a/btcpy/structs/script.py b/btcpy/structs/script.py index 0b489a1..f9090b1 100644 --- a/btcpy/structs/script.py +++ b/btcpy/structs/script.py @@ -699,6 +699,22 @@ class MultisigScript(ScriptPubKey): template = '<>+ OP_CHECKMULTISIG' + @classmethod + def verify(cls, bytes_): + from .crypto import PublicKey + parser = ScriptParser(bytes_) + if not bytes_: + raise WrongScriptTypeException('Empty script') + try: + m, *pubkeys, n = [data for data in parser.match(cls.template)] + except UnexpectedOperationFound as exc: + raise WrongScriptTypeException(str(exc)) + + if len(pubkeys) == 0 or len(pubkeys) != int(n): + raise WrongScriptTypeException('Non-matching N and number of pubkeys') + + return [int(m), *[PublicKey(pubkey.data) for pubkey in pubkeys], int(n)] + def __init__(self, *args): """ :param args: if one arg is provided that is interpreted as a precompiled script which needs @@ -706,7 +722,6 @@ def __init__(self, *args): and `n` are extracted and saved. If more than one arg is provided, we assume that the parameters are `m, pubkey1, ..., pubkeyn, n`. """ - from .crypto import PublicKey if len(args) == 0: raise TypeError('Wrong number of params for MultisigScript __init__: {}'.format(len(args))) @@ -715,9 +730,6 @@ def __init__(self, *args): script = args[0] super().__init__(script.body) m, *pubkeys, n = self.verify(script.body) - m = int(m) - pubkeys = [PublicKey(pk.data) for pk in pubkeys] - n = int(n) else: m, *pubkeys, n = args diff --git a/tests/data/unknownscripts.json b/tests/data/unknownscripts.json new file mode 100644 index 0000000..bd598a0 --- /dev/null +++ b/tests/data/unknownscripts.json @@ -0,0 +1,17 @@ +["5152ae", + "6a", + "6a4b", + "6368", + "4bb175", + "4bb275", + "4bac", + "004b", + "a94b87", + "76a94b88ac", + "6a", + "ac", + "00", + "b175", + "b275", + "a987", + "76a988ac"] diff --git a/tests/unit.py b/tests/unit.py index 085cebb..6c677a9 100644 --- a/tests/unit.py +++ b/tests/unit.py @@ -36,6 +36,7 @@ def get_data(filename): transactions = get_data('rawtxs') scripts = get_data('scripts') +unknownscripts = get_data('unknownscripts') keys = get_data('xkeys') segwit_valid_addresses = get_data('segwit_addr_valid') segwit_invalid_addresses = get_data('segwit_addr_invalid') @@ -51,6 +52,14 @@ def get_data(filename): priv_pub_hash_addr_p2pkh_segwit = get_data('priv_pub_hash_addr_p2pkh_segwit') +class TestUnknownScript(unittest.TestCase): + + def test(self): + for script in unknownscripts: + result = ScriptBuilder.identify(unhexlify(script)) + self.assertTrue(isinstance(result, UnknownScript)) + + class TestPrivPubHashAddrP2pkhSegwit(unittest.TestCase): def test(self):