Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefix notation for logical compound conditions #3140

Merged
merged 10 commits into from
Jun 15, 2023

Conversation

derekpierre
Copy link
Member

@derekpierre derekpierre commented Jun 9, 2023

Type of PR:

  • Bugfix
  • Feature
  • Documentation
  • Other

Required reviews:

  • 1
  • 2
  • 3

What this does:
Replaces infix notation with prefix notation for logical combinations of conditions.

Issues fixed/closed:
Fixes #3058

Why it's needed:
This is needed to disambiguate logical groupings as outlined in #3058 .

For example:

{
   "operator":"or",
   "operands":[
      {
         "chain":131277322940537,
         "method":"ownerOf",
         "parameters":[
            5954
         ],
         "returnValueTest":{
            "value":":userAddress",
            "comparator":"=="
         },
         "contractAddress":"0xaDD9D957170dF6F33982001E4c22eCCdd5539118",
         "standardContractType":"ERC721"
      },
      {
         "chain":131277322940537,
         "method":"blocktime",
         "returnValueTest":{
            "value":0,
            "comparator":">"
         }
      },
      {
         "operator":"and",
         "operands":[
            {
               "chain":131277322940537,
               "method":"eth_getBalance",
               "parameters":[
                  ":userAddress"
               ],
               "returnValueTest":{
                  "value":1000000000000000000000000,
                  "comparator":"=="
               }
            },
            {
               "chain":131277322940537,
               "method":"balanceOf",
               "parameters":[
                  ":userAddress"
               ],
               "returnValueTest":{
                  "value":0,
                  "comparator":"=="
               },
               "contractAddress":"0xaDD9D957170dF6F33982001E4c22eCCdd5539118",
               "standardContractType":"ERC20"
            }
         ]
      }
   ]
}

Notes for reviewers:

What should reviewers focus on?
Is there a particular commit/function/section of your PR that requires more attention from reviewers?

This will have downstream effect on nucypher-ts, and how logical operators are used there - nucypher/taco-web#224

derekpierre added a commit to derekpierre/nucypher that referenced this pull request Jun 11, 2023
@derekpierre derekpierre changed the title [WIP] Prefix notation for logical compound conditions Prefix notation for logical compound conditions Jun 12, 2023
@derekpierre derekpierre self-assigned this Jun 12, 2023
@derekpierre derekpierre marked this pull request as ready for review June 12, 2023 13:20
@jMyles
Copy link
Contributor

jMyles commented Jun 12, 2023

Since the convo on #3058 stalled, I just want to confirm:

Are we OK with proceeding with a format which is not (easily) checkable on-chain?

@derekpierre
Copy link
Member Author

derekpierre commented Jun 12, 2023

Since the convo on #3058 stalled, I just want to confirm:

Are we OK with proceeding with a format which is not (easily) checkable on-chain?

One thing for sure is that the infix format (current status quo) is something that needed to be removed/deprecated. Other options can be explored/discussed, but the current conditions format could not remain due to the ambiguity of logical operations and the ongoing technical debt of also supporting infix. Moving from infix notation to prefix notation wasn't too bad of a lift.

#3058 was mainly about infix vs prefix, and expanded into a discussion about the language used for conditions. Totally understandable since they are in the same realm, but ultimately the condition language itself is a much broader discussion than intended for that issue, and perhaps we can continue that higher level conversation in a more focused issue since there are still many unknowns to resolve/consider:

  • Can something like a BytecodeCondition type simply just be another type of condition like RPC/Time/Contract - could that then be a decision of adopters to use instead of us forcing it?
  • How does it affect our goal for multichain capabilities?
  • What if you need compound conditions across different non-compatible chains? You're back to needing a common language to allow for compounded conditions across these anyway...
  • Do we need conditions to be verifiable by an on-chain referee?
  • Is it compatible with multicall work (@manumonti , @cygnusv )?
  • How does not using a JSON language affect the UX? How easily can apps generate the bytecode(s) they need for the condition(s)?

Filed #3144 to continue that discussion.

Copy link
Member

@manumonti manumonti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like this prefix syntax for compound conditions. In my opinion, the code is pretty much clean in this way and error-proof. Great work. Just some thoughts in my review.

nucypher/policy/conditions/lingo.py Show resolved Hide resolved
nucypher/policy/conditions/lingo.py Show resolved Hide resolved
tests/unit/conditions/test_compound_conditions.py Outdated Show resolved Hide resolved
nucypher/policy/conditions/lingo.py Show resolved Hide resolved
@manumonti
Copy link
Member

I do not see any inconvenience for multicall using this format. Actually, the simplicity of this code is better for implementing multicall.

Copy link
Member

@manumonti manumonti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! ✌️

examples/tdec/simple_tdec.py Outdated Show resolved Hide resolved

ciphertext = self.encrypt_for_dkg(plaintext=plaintext,
conditions=conditions)
def encrypt_for_dkg_and_produce_decryption_request(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a better name IMO, but outside the scope of this PR.

nucypher/network/server.py Outdated Show resolved Hide resolved
nucypher/network/server.py Outdated Show resolved Hide resolved
Comment on lines -1 to -23
import json
from typing import List

from nucypher_core import ReencryptionRequest, Conditions

from nucypher.policy.conditions.lingo import ConditionLingo


def _serialize_rust_lingos(lingos: List[Conditions]) -> Conditions:
lingo_lists = list()
for lingo in lingos:
if lingo:
lingo = json.loads((str(lingo)))
lingo_lists.append(lingo)
rust_lingos = Conditions(json.dumps(lingo_lists))
return rust_lingos


def _deserialize_rust_lingos(reenc_request: ReencryptionRequest):
"""Shim for nucypher-core lingos"""
json_lingos = json.loads(str(reenc_request.conditions))
lingo = [ConditionLingo.from_list(lingo) if lingo else None for lingo in json_lingos]
return lingo
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such a relief to finally realize this removal!

Copy link
Member

@KPrasch KPrasch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent PR 👏🏻 I'm in favor of merging, although since this is mission-critical code that has gained complexity (rightfully so) and the conditions format is predictable we might be able to develop a fuzzing strategy to ensure logical correctness in all possible cases.

_operator = data["operator"]
except KeyError:
raise InvalidLogicalOperator(f"Invalid operator data: {data}")
class CompoundAccessControlCondition(AccessControlCondition):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not specific to this PR:

  • Should we put a ceiling on the number of elements in conditions? Something in the order of ~100. We could short-circuit it too: evaluate the first 100, and if there exists another then return False.
  • Seems like a possible DoS vector. WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a very sensible restriction, in fact I'd make the ceiling much lower, perhaps 5-10 elements since each call adds o(n) runtime at minimum, and maps directly onto node operational costs. Even beyond that, we may consider some form of rate limiting. All of which needs to be addressed in another PR IMO.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice thought!

We can potentially limit the length of the list of conditions, and raise an InvalidCondition exception if too long during object creation/schema validation.

I'd rather do that than allow the object to be created then fail during execution because it is too long - could be too confusing to the user. That being said, technically you can work around it by having repeated nested compound conditions each with operands under the limit.

So maybe some kind of overall count needs to be calculated during creation...?

Some good thoughts here. Let's tackle it in another PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's definitely both client and server validation involved there, I think this is often the case with the condition module as a whole.

I agree that's material for another contribution. I'll be interested in scoping this, also for the purpose of implementation in nucypher-ts.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 - filed #3148.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to throw is out there, another way to enforce a limitation is by capping the number of bytes.

Copy link
Contributor

@piotr-roslaniec piotr-roslaniec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple of questions.

LGTM 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Completed
Development

Successfully merging this pull request may close these issues.

Decide between infix & prefix notation for genesis
5 participants