polkascan / py-scale-codec

Python SCALE-Codec
https://polkascan.github.io/py-scale-codec/
Apache License 2.0
54 stars 52 forks source link

Unable to create as_multi calls due to encoding rules of OpaqueCall's #20

Open CodeForcer opened 4 years ago

CodeForcer commented 4 years ago

Currently the encoding rules for as_multi require the call to be serialised:

as_multi = ScaleDecoder.get_decoder_class("Call", metadata=kusama.metadata)
transfer = ScaleDecoder.get_decoder_class("OpaqueCall", metadata=kusama.metadata)
transfer.encode(
    {
        "call_module": "Balances",
        "call_function": "transfer",
        "call_args": {"dest": "CofvaLbP3m8PLeNRQmLVPWmTT7jGgAXTwyT69k2wkfPxJ9V", "value": 10000000000000},
    }
)
as_multi.encode(
    {
        "call_module": "Multisig",
        "call_function": "as_multi",
        "call_args": {
            "call": str(transfer),
            "maybe_timepoint": {"height": 3012294, "index": 3},
            "other_signatories": sorted(['D2bHQwFcQj11SvtkjULEdKhK4WAeP6MThXgosMHjW9DrmbE', 'CofvaLbP3m8PLeNRQmLVPWmTT7jGgAXTwyT69k2wkfPxJ9V']),
            "threshold": 2,
            "store_call": True,
            "max_weight": 10,
        },
    }
)

However, presumably due to a metadata change, the same parameter is now only accepted in dictionary format:

  File "/Users/nathan/.pyenv/versions/3.8.1/envs/ksmapi/lib/python3.8/site-packages/ksmutils/helper.py", line 161, in as_multi_signature_payload
    as_multi.encode(
  File "/Users/nathan/.pyenv/versions/3.8.1/envs/ksmapi/lib/python3.8/site-packages/scalecodec/base.py", line 298, in encode
    self.data = self.process_encode(value)
  File "/Users/nathan/.pyenv/versions/3.8.1/envs/ksmapi/lib/python3.8/site-packages/scalecodec/types.py", line 1180, in process_encode
    data += arg_obj.encode(param_value)
  File "/Users/nathan/.pyenv/versions/3.8.1/envs/ksmapi/lib/python3.8/site-packages/scalecodec/base.py", line 298, in encode
    self.data = self.process_encode(value)
  File "/Users/nathan/.pyenv/versions/3.8.1/envs/ksmapi/lib/python3.8/site-packages/scalecodec/types.py", line 1188, in process_encode
    return super().process_encode(str(call_obj.encode(value)))
  File "/Users/nathan/.pyenv/versions/3.8.1/envs/ksmapi/lib/python3.8/site-packages/scalecodec/base.py", line 298, in encode
    self.data = self.process_encode(value)
  File "/Users/nathan/.pyenv/versions/3.8.1/envs/ksmapi/lib/python3.8/site-packages/scalecodec/types.py", line 1157, in process_encode
    if call_module.name == value['call_module'] and call_function.name == value['call_function']:
TypeError: string indices must be integers
FAILED

I submitted a PR with one patch for the error, but ideally this could be handled at the point of as_multi encoding

arjanz commented 4 years ago

There is a special OpaqueCall class that takes a dict as input for encoding and internally wraps the output so it is in fact a Bytes: https://github.com/polkascan/py-scale-codec/blob/d6aac22795089992bee778e1872903d407f7af4d/scalecodec/types.py#L1186 So basically, all you have to do is rewrite your as_multi to:

as_multi = ScaleDecoder.get_decoder_class("Call", metadata=self.metadata_decoder)

as_multi.encode(
    {
        "call_module": "Multisig",
        "call_function": "as_multi",
        "call_args": {
            "call": {
                "call_module": "Balances",
                "call_function": "transfer",
                "call_args": {"dest": "CofvaLbP3m8PLeNRQmLVPWmTT7jGgAXTwyT69k2wkfPxJ9V", "value": 10000000000000},
            },
            "maybe_timepoint": {"height": 3012294, "index": 3},
            "other_signatories": sorted(['D2bHQwFcQj11SvtkjULEdKhK4WAeP6MThXgosMHjW9DrmbE',
                                         'CofvaLbP3m8PLeNRQmLVPWmTT7jGgAXTwyT69k2wkfPxJ9V']),
            "threshold": 2,
            "store_call": True,
            "max_weight": 10,
        },
    }
)
Shawncomst commented 2 years ago

Currently the encoding rules for as_multi require the call to be serialised:

as_multi = ScaleDecoder.get_decoder_class("Call", metadata=kusama.metadata)
transfer = ScaleDecoder.get_decoder_class("OpaqueCall", metadata=kusama.metadata)
transfer.encode(
    {
        "call_module": "Balances",
        "call_function": "transfer",
        "call_args": {"dest": "CofvaLbP3m8PLeNRQmLVPWmTT7jGgAXTwyT69k2wkfPxJ9V", "value": 10000000000000},
    }
)
as_multi.encode(
    {
        "call_module": "Multisig",
        "call_function": "as_multi",
        "call_args": {
            "call": str(transfer),
            "maybe_timepoint": {"height": 3012294, "index": 3},
            "other_signatories": sorted(['D2bHQwFcQj11SvtkjULEdKhK4WAeP6MThXgosMHjW9DrmbE', 'CofvaLbP3m8PLeNRQmLVPWmTT7jGgAXTwyT69k2wkfPxJ9V']),
            "threshold": 2,
            "store_call": True,
            "max_weight": 10,
        },
    }
)

However, presumably due to a metadata change, the same parameter is now only accepted in dictionary format:

  File "/Users/nathan/.pyenv/versions/3.8.1/envs/ksmapi/lib/python3.8/site-packages/ksmutils/helper.py", line 161, in as_multi_signature_payload
    as_multi.encode(
  File "/Users/nathan/.pyenv/versions/3.8.1/envs/ksmapi/lib/python3.8/site-packages/scalecodec/base.py", line 298, in encode
    self.data = self.process_encode(value)
  File "/Users/nathan/.pyenv/versions/3.8.1/envs/ksmapi/lib/python3.8/site-packages/scalecodec/types.py", line 1180, in process_encode
    data += arg_obj.encode(param_value)
  File "/Users/nathan/.pyenv/versions/3.8.1/envs/ksmapi/lib/python3.8/site-packages/scalecodec/base.py", line 298, in encode
    self.data = self.process_encode(value)
  File "/Users/nathan/.pyenv/versions/3.8.1/envs/ksmapi/lib/python3.8/site-packages/scalecodec/types.py", line 1188, in process_encode
    return super().process_encode(str(call_obj.encode(value)))
  File "/Users/nathan/.pyenv/versions/3.8.1/envs/ksmapi/lib/python3.8/site-packages/scalecodec/base.py", line 298, in encode
    self.data = self.process_encode(value)
  File "/Users/nathan/.pyenv/versions/3.8.1/envs/ksmapi/lib/python3.8/site-packages/scalecodec/types.py", line 1157, in process_encode
    if call_module.name == value['call_module'] and call_function.name == value['call_function']:
TypeError: string indices must be integers
FAILED

38.975 I submitted a PR with one patch for the error, but ideally this could be handled at the point of as_multi encoding