diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 9c9abbbea..1f1c917c9 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -338,7 +338,7 @@ class UnixDomainSocketRpc(object): this_id = f'{cmdprefix}/{this_id}' return this_id - def call(self, method, payload=None, cmdprefix=None): + def call(self, method, payload=None, cmdprefix=None, filter=None): """Generic call API: you can set cmdprefix here, or set self.cmdprefix before the call is made. @@ -379,6 +379,9 @@ class UnixDomainSocketRpc(object): "id": this_id, } + if filter is not None: + request["filter"] = filter + self._writeobj(sock, request) while True: resp, buf = self._readobj(sock, buf) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 927f6e8ce..61d773aea 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -678,13 +678,14 @@ class PrettyPrintingLightningRpc(LightningRpc): self.jsonschemas = jsonschemas self.check_request_schemas = True - def call(self, method, payload=None, cmdprefix=None): + def call(self, method, payload=None, cmdprefix=None, filter=None): id = self.get_json_id(method, cmdprefix) schemas = self.jsonschemas.get(method) self.logger.debug(json.dumps({ "id": id, "method": method, - "params": payload + "params": payload, + "filter": filter, }, indent=2)) # We only check payloads which are dicts, which is what we @@ -698,13 +699,13 @@ class PrettyPrintingLightningRpc(LightningRpc): testpayload[k] = v schemas[0].validate(testpayload) - res = LightningRpc.call(self, method, payload, cmdprefix) + res = LightningRpc.call(self, method, payload, cmdprefix, filter) self.logger.debug(json.dumps({ "id": id, "result": res }, indent=2)) - if schemas and schemas[1]: + if schemas and schemas[1] and not filter: schemas[1].validate(res) return res diff --git a/tests/test_misc.py b/tests/test_misc.py index 9fff772fa..e4f227f6c 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2812,6 +2812,49 @@ def test_torv2_in_db(node_factory): l1.start() +def test_field_filter(node_factory, chainparams): + l1, l2 = node_factory.get_nodes(2) + + addr1 = l1.rpc.newaddr('bech32')['bech32'] + addr2 = l1.rpc.newaddr('p2sh-segwit')['p2sh-segwit'] + inv = l1.rpc.invoice(123000, 'label', 'description', 3700, [addr1, addr2]) + + # Simple case: single field + dec = l1.rpc.call('decodepay', {'bolt11': inv['bolt11']}, filter={"currency": True}) + assert dec == {"currency": chainparams['bip173_prefix']} + + # Two fields + dec = l1.rpc.call('decodepay', {'bolt11': inv['bolt11']}, filter={"currency": True, "payment_hash": True}) + assert dec == {"currency": chainparams['bip173_prefix'], + "payment_hash": inv['payment_hash']} + + # Nested fields + dec = l1.rpc.call('decodepay', {'bolt11': inv['bolt11']}, + filter={"currency": True, + "payment_hash": True, + "fallbacks": [{"type": True}]}) + assert dec == {"currency": chainparams['bip173_prefix'], + "payment_hash": inv['payment_hash'], + "fallbacks": [{"type": 'P2WPKH'}, {"type": 'P2SH'}]} + + # Nonexistent fields. + dec = l1.rpc.call('decodepay', {'bolt11': inv['bolt11']}, + filter={"foobar": True}) + assert dec == {} + + # Bad filters + dec = l1.rpc.call('decodepay', {'bolt11': inv['bolt11']}, + filter={"currency": True, + "payment_hash": True, + "fallbacks": {'type': True}}) + assert dec['warning_parameter_filter'] == '.fallbacks is an array' + + # Plugins ignore filters! + res = l1.rpc.call('decode', {'string': inv['bolt11']}, + filter={"currency": True}) + assert 'type' in res + + def test_checkmessage_pubkey_not_found(node_factory): l1 = node_factory.get_node()