polkascan / py-substrate-interface

Python Substrate Interface
https://polkascan.github.io/py-substrate-interface/
Apache License 2.0
240 stars 114 forks source link

Passing an empty list as an argument results in an exception in 1.3.3 #259

Closed brandonpille closed 1 year ago

brandonpille commented 1 year ago

The exception occurs when calling compose_call:

 call = substrate.compose_call("TfgridModule", "create_farm",
                               {
                                    "name": f"{name}",
                                    "public_ips": public_ips
                               })

The extrinsic contains two arguments: name and public_ips. public_ips is a BoundedVec of a struct containing two string properties.

Here is the tracebak:

Traceback (most recent call last):
  File "/work/tfchain/substrate-node/tests/TfChainClient.py", line 288, in create_node
    call = substrate.compose_call(
  File "/home/brandon/.local/lib/python3.10/site-packages/substrateinterface/base.py", line 1618, in compose_call
    call.encode({
  File "/home/brandon/.local/lib/python3.10/site-packages/scalecodec/base.py", line 723, in encode
    self.data = self.process_encode(self.value_serialized)
  File "/home/brandon/.local/lib/python3.10/site-packages/scalecodec/types.py", line 1464, in process_encode
    data += arg_obj.encode(param_value)
  File "/home/brandon/.local/lib/python3.10/site-packages/scalecodec/base.py", line 723, in encode
    self.data = self.process_encode(self.value_serialized)
  File "/home/brandon/.local/lib/python3.10/site-packages/scalecodec/types.py", line 567, in process_encode
    raise ValueError('Element count of value ({}) doesn\'t match type_definition ({})'.format(
ValueError: Element count of value (0) doesn't match type_definition (1)
arjanz commented 1 year ago

Which network are you connecting to? Is their a public endpoint available? (So I can try to reproduce)

brandonpille commented 1 year ago

Unfortunately no. I'm working for ThreeFold Tech and this is part of a new feature. Though this is happening for all arguments that are lists.

arjanz commented 1 year ago

What you can try is to see what the type decomposition is of the call params, as shown here: https://github.com/polkascan/py-substrate-interface#type-decomposition-of-call-params

If that doesn't help, maybe you can share the outcome of call_function.get_param_info() or ideally if possible substrate.rpc_request("state_getMetadata", []) so I can debug these specific runtime types.

brandonpille commented 1 year ago

Here are the results of get_param_info():

{
    "name": {
        "composite": {
            "fields": [{
                "name": None, 
                "type": 11, 
                "typeName": "Vec<T>", 
                "docs": [], 
                "value": {
                    "sequence": {
                        "type": 2, 
                        "value": {
                            "primitive": "u8"
                        }
                    }
                }
            }]
        }
    }, 
    "public_ips": {
        "composite": {
            "fields": [{
                "name": None, 
                "type": 220, 
                "typeName": "Vec<T>", 
                "docs": [], 
                "value": {
                    "sequence": {
                        "type": 219, 
                        "value": {
                            "composite": {
                                "fields": [{
                                    "name": "ip", 
                                    "type": 48, 
                                    "typeName": 
                                    "IP", 
                                    "docs": [], 
                                    "value": {
                                        "composite": {
                                            "fields": [{
                                                "name": None, 
                                                "type": 11, 
                                                "typeName": "Vec<T>", 
                                                "docs": [], 
                                                "value": {
                                                    "sequence": {
                                                        "type": 2,
                                                        "value": {
                                                            "primitive": "u8"
                                                        }
                                                    }
                                                }
                                            }]
                                        }
                                    }
                                }, {
                                    "name": "gw", 
                                    "type": 50, 
                                    "typeName": "GW", 
                                    "docs": [], 
                                    "value": {
                                        "composite": {
                                            "fields": [{
                                                "name": None, 
                                                "type": 11, 
                                                "typeName": "Vec<T>", 
                                                "docs": [], 
                                                "value": {
                                                    "sequence": {
                                                        "type": 2, 
                                                        "value": {
                                                            "primitive": "u8"
                                                        }
                                                    }
                                                }
                                            }]
                                        }
                                    }
                                }]
                            }
                        }
                    }
                }
            }]
        }
    }
}
brandonpille commented 1 year ago

This is the result of the rpc_request. result.txt

arjanz commented 1 year ago

I got the call encoded with a workaround, there is indeed something odd about the required nested lists I will have to look into further (seems like it should be just one list):

call = substrate.compose_call("TfgridModule", "create_farm", {
    "name": "test",
    "public_ips": [[{
        "ip": "test",
        "gw": "test"
    }]]
})

Also works with nested empty list:

    "name": "test",
    "public_ips": [[]]
})
arjanz commented 1 year ago

Just had another look, probably has something to do with the "composite" field seemingly decorating the "sequence" field right under "public_ips". It doesn't seem to have a direct purpose and is an unnamed composite field, resulting in a tuple. I could consider always removing those "decorator" fields, but I'll have to review the impact of this.

brandonpille commented 1 year ago

Yes indeed a nested list works indeed. But just passing an empty list does not work. It would be amazing if you could take a look at it thanks!

arjanz commented 1 year ago

Fix released in https://github.com/polkascan/py-scale-codec/releases/tag/v1.0.48