Shielded Kudos Pt. 3: KL constraints

This post aims to describe the KL logic constraints of the Kudos Application Design from this post. It doesn’t include Receive constraints, Intent constraints, or DL examples.

Kudos resource structure

logic KL logic VK
label DL logic VK
value Owner PK
  • DL might require additional fields in the label and value. For that reason, we assume label and value openings being maps, but from the KL perspective, so far, only one sub-field is needed for each (I might have missed something).

Constraints

Depending on the sort of the self resource’s tag type and ephemerality, it can only be a part of certain functions. For example, ephemeral resources are created only in Burn.

Persistent resources can be a part of different functions. The diagram below describes how to differentiate between them.

1. Issue (required resources in the action: 3, kudo resources: 2)

  1. The DL is triggered[1]:
    1. There is a resource R_{DL} in the same action s.t. R_DL.logic = self.label.dl_vk
  2. If self is consumed:
    1. self.is_ephemeral = True
    2. There is another created resource R_K in the transaction s.t.:
      1. R_K.logic = self.logic
      2. R_K.label.dl_vk = self.label.dl_vk[2]
      3. R_K.is_ephemeral = False
  3. If self is created:
    1. self.is_ephemeral = False
    2. There is another consumed resource R_K in the transaction s.t.:
      1. R_K.logic = self.logic
      2. R_K.label.dl_vk = self.label.dl_vk
      3. R_K.is_ephemeral = True

2. Burn (required resources in the action: 3, kudo resources: 2)

  1. DL is checked

    1. there is a resource R_{DL} in the same action s.t. R_DL.logic = self.label.dl_vk
  2. If self is created:

    1. self.is_ephemeral = True
    2. There is another consumed resource R_K in the transaction s.t.:
      1. R_K.logic = self.logic
      2. R_K.label.dl_vk = self.label.dl_vk
      3. R_K.is_ephemeral = False
  3. If self is consumed:

    1. self.is_ephemeral = False
    2. There is another created resource R_K in the transaction s.t.:
      1. R_K.logic = self.logic
      2. R_K.label.dl_vk = self.label.dl_vk
      3. R_K.is_ephemeral = True

3. Transfer (required resources in the action: 4, kudo resources: 2)

  1. DL is checked
    1. there is a resource R_{DL} in the same action s.t. R.logic = self.label.dl_vk
  2. If self is created:
    1. self.is_ephemeral = False

    2. There is another consumed resource R_K in the transaction s.t.:

      1. R_K.logic = self.logic
      2. R_K.label.dl_vk = self.label.dl_vk
      3. R_K.is_ephemeral = False
    3. Receive logic is triggered:

      1. There is another created resource R_{receive} in the transaction s.t.:
        1. Signature.Verify(Sig,R_receive.logic, self.value.owner) = True
  3. If self is consumed:
    1. self.is_ephemeral = False
    2. There is another created resource R_{K} in the transaction s.t.:
      1. R_K.logic = self.logic
      2. R_K.label.dl_vk = self.label.dl_vk
      3. R_K.is_ephemeral = False

4. Swap (required resources in the action: 3, kudo resources: 1)

  1. DL is checked

    1. there is a resource R_{DL} in the same action s.t.:
      1. R_DL.logic = self.label.dl_vk
  2. self.is_ephemeral = False

  3. isConsumed = True

  4. Intent logic is triggered:

    1. There is another created resource R_I in the transaction s.t.:
      1. Signature.Verify(Sig, R_I.logic, self.value.owner) = True

Instances and Witnesses

  • Instance:
    • as specified in the specs
    • action.applicationData[tag] contains:
      • For Transfer: the signature over the receive logic (1.4)
      • For Swap: the signature over the intent logic (1.2)[3]
  • Witness:
    • We only need the resource objects

Action sizes

We often like to think of actions as data structures of arbitrary size, but in the shielded case the circuit can take only a limited amount of resources in the action since the circuit is fixed. One approach would be to fix the expected amount of resources in the action to the minimal necessary, another - to set an upper bound. The first option makes verification a bit easier: there are not a lot of resources to go through. One thing we need to keep in mind is that the same logic governs different functions (Issue, Burn, Transfer, Swap), and some of them require less resources. We need to make sure this cannot be exploited. The upper bound approach gives us a bit more programmability, but makes the verification a bit more difficult: more resources - more processing + more opportunities to exploit the structure (if flawed).

In the post I specified the minimal amount of resources required, but didn’t make an explicit decision about the approach to take.

Redundancy

It is possible that the constraints can be simplified. I didn’t try to optimise for that.


  1. this is a check that must be present in all transaction functions ↩︎

  2. since right now label only contains DL VK, this comparison is equivalent to comparing labels. I’m not sure if there is a case when we want to allow different labels with the same DL VK to interoperate, but I decided to be specific just in case. Note that in that case the balance would have to be taken care of explicitly in the logic since the delta check wouldn’t work for resources of different kinds. ↩︎

  3. We might want to move the signatures to the resources, as described here, which would be convenient, but I didn’t think much about it yet. ↩︎

Shouldn’t suffice to enforce ephemerality of consumed resources? Why counterparts must be in the same action? Balancing will enforce counterpart inclusion, and, in principle, I don’t see why one can’t issue an ephemeral kudo, i.e. issue nothing (constraint 2.1). In other words, do we need constraint 2 at all? Similar for constraint 1 of burning (burn nothing).

Also, R_{DL} resource could be enforced to be ephemeral (not strictly needed I think, only for storage efficiency reasons).

Would the logic of the R_{DL} constraint who can issue? I think the authorization mechanism is the key part of issuing.

You probably mean action instead of transaction (and in swap).

I think constraint 1.2 is equivalent to constraint 2.1 (and 2.2 to 1.1). Created and consumed resources could then be in different actions.

Just confirming that this logic deals with change and transfering ammounts from different kudo resources? (I think it does…)

Constraint 1.3: could we skip triggering receive logic if the public key equals some of the old (consumed) public keys? The sender does not need to authorize themselve. This would allow to not include as many R_{receive} resources as different new (created) public keys, probably saving proof generation runtime. With this ‘if’, the consumed and created resources really have inter-dependent state so must be in the same action.

As in the issue case, would the logic of R_{DL} constraint who can transfer and to whom? e.g. a signature on the pk of the created resource, under the pk of the consumed resource. I’d like to get the full picture here.

I would say go for an upper bound to account for transferring from/to different kudo resources.

Which branch of the logic is triggered based on the self resource? It seems the function must be somehow encoded in the resource?

Thank you for your post! I have a couple of questions.

  1. Who owns the newly issued resources? Should we trigger the receiving logic for them?
  2. How do you constrain who can issue? I assume a special pk/authorization is used, but it’s not in the label of KL or DL and not constrained by KL logic.
  3. How many resources does one KL need? It should be a fixed number for all cases (Issue, Burn, Transfer, Swap), even though some cases require fewer. Based on your design, the KL always uses the maximum of 4 resources, as it cannot handle variable numbers of resources and doesn’t know which case it will apply to.
  4. I’m confused. I sense that you are defining actions, like transfers, in the KL. This could relate to @kike’s question: “Why must counterparts be in the same action?” If constraints for resource creation and consumption are properly defined, the balance checks can complete transfers automatically regardless of whether resources are in the same action or not.
  5. Related to 4, it seems I can’t create the simple example: I consume my appleKudo and create an orangeKudo for me without an intent-carrying resource in one action.
  6. I didn’t figure out why the intent logic needs to be explicitly triggered by the KL. If I provide sth(consume some resources) and have an intent(create an intent-carrying resource) in one action, the integrity of compliance proofs ensures the intent-carrying resource can not be tampered with and balance checks ensure the intent-carrying resource will be consumed and satisfied. Could you please clarify the reasoning behind adding a signature verification to trigger the intent logic?

Looking forward to your reply and thank you beforehand.

1 Like

Because if they are not in the same action, they are out of the logic’s scope and the logic cannot be aware of them.

You have a persistent created resource. The balancing resource might be in another action. What do you check on that resource? How do you know if you are issuing it or it was transferred? The requirements differ depending on those. And keep in mind that the DL logic will specify more constraints.

Also why would you need to allow them being in separate actions? Is there any value in that?

It is ephemeral by design, but it doesn’t affect the storage in the shielded case since the storage doesn’t know which resources are ephemeral.

It might. The thing is that there is no single DL logic by design. Some logics might constraint it, some will not.

What do you mean by different kudo resources? The denomination is the same.

In the same action?

I thought about this and decided to leave it out for generality. I find it not very likely that the sender sends themselves exactly the same resource (“change” function is not supported by transfer) to the exact same public key. If the key changes, it needs to be authorised.

The function cannot be encoded in the resource. The branch is triggered depending on the combination (created, consumed) of resources in the action. The diagrams above describe the conditions for determining the case being checked (which wouldn’t work if the resources are in different actions)

That is a good question. I assume the resource is owned by the issuer, but this might be further constrained in DL. In particular, DL might enforce calling receive logic (like KL does) if it allows issuing for someone else.

The issuance constraint is specific to DL. I imagine using a pk authorisation, and if necessary, the DL will require resources to include some denomination owner/issuer-related field in the label/value.

The current “form” of the kudo resource is the minimal form. Specific DLs can require additional structure of label/value to verify the additional constraints.

As I calculated it here, it is 4. I would imagine adding 1-2 extra slots just in case, but since KL doesn’t change, it might be pointless. Although this design doesn’t include some transaction functions such as change (1 consumed 2 created) or merge (2 consumed, 1 created) which we might want to add too. They are pretty straightforward, but are not included explicitly in the design yet. Those transaction functions will take in more than 4 resources (the persistent ones + balance + DL + possibly receive).

  1. Is there any situation when we might want them to be in different actions?
  2. I did it because it I couldn’t think of when it might be useful but that puts all resources in the same scope, which is useful for logic verification.

I’m not sure I understood what you mean. You are not supposed to be able to consume one denomination to create another denomination by default.

The KL verifies that the owner of the consumed resources authored the intent, not someone else. For example, a user might give a solver some kudos to consume and the intent, and the solver will consume the kudos but replace the intent with their own

Thank you for your reply

The KL triggers the DL but only constrains the dl_vk. I can create a DL proof with dl_vk and a tampered label/value, such as the issuer’s pk. You will also revise the KL once the DL structure is finalized, right?

For example, I can’t consume my appleKudo and create an orangeKudo in one action. Someone else will take my appleKudo and provide an orangeKudo in another action. You said it’s not allowed by default, but this is the simplest example in Specs.

I’m starting to understand your design. You’re defining and listing all transaction functions in KL. If a function type or its associated resources aren’t included in KL logic, they aren’t allowed. For example, I can’t consume an appleKudo and create an orangeKudo in one action, nor can I perform changes like (1 consumed, 2 created) or merges (2 consumed, 1 created) as you mentioned unless explicitly added to KL. Additionally, the application might not flexibly interact with other applications/resources. To do so, we must use the Swap function you defined. Did I misunderstand anything?

From my original thoughts, but it could be wrong. we only need to focus on designing the rules for resource creation and consumption in the resource logic. The resource logic does not need to understand specific functions (e.g., transfer, swap, merge). For complex functions, we can use ephemeral resources (intents or logic) to connect and glue different resources and applications in one action or different actions. Users and developers can combine any types and quantities of resources in actions to achieve functions (e.g., transfer, swap, merge), as long as the transaction is balanced in the end. Issuance and burning are defining special rules for creating or consuming ephemeral resources of specific kinds.

What do you mean by giving a solver some kudos to consume and the intent? If you mean giving all the witnesses to the solver, the solver could easily steal the kudos. If you mean consuming some kudos and creating an intent-carrying resource(only proofs and necessary info are available to the solver), I don’t think the solver can tamper the intent-carrying resource because the solver can not recreate the compliance proofs without knowing all the witnesses of kudos.

The DL structure is never finalised, in some sense, we have a single KL that works with multiple DLs.

KL doesn’t constraint the issuer’s pk because we might not need it at all for certain denominations. DL, being a part of the same action, takes as input all the same resources. If the prover passes tampered resources as input, the proofs won’t verify because the verifier will pass the actual cm/nf to the DL.

So, the idea is that KL verifies some constraints that we enforce for all denominations and ensures that DL is verified for the same resources. Then different DLs verify specific constraints for resources associated with that DL, while all resources share the same main logic.

None of the functions described in the post include a function that allows consuming one denomination token and create a different denomination token. If we are talking about swaps, the atomic action contains only the kudo resource (appleKudo), DL, and intent carrier. So there is nothing in the proposed design that prevents appleKudo and orangeKudo in separate actions.

Let’s imagine we want to do a swap without intents, with a friend we trust. Alice’s action contains her appleKudo consumed, a created appleKudo for her friend Bob, appleDL, and Bob’s receive resource. The second action, created by Bob, contains his orangeKudo consumed, a created orangeKudo for Alice, orangeDL, and Alice’s receive resource. Does this example make sense?

Yes, precisely

You are right about merge/change, but I think it makes sense to define them explicitly. After that, I think, there is no issue about flexibility of interaction, the only limitation comes from the expected action size.

Other applications can interact with the kudo application without the kudo application being aware of it by design. For example, there is a storage application that uses kudos as payment tokens. The storage application logic would only be valid if it detects some kudo resources in some action, but the kudo application itself doesn’t have to be aware of it. To allow this, though, we do need to allow kudo-related actions to be a bit bigger (the kudo application doesn’t need to “understand” these extra resources in the action)

I think it would be cool to do that, but I don’t see a way how to prevent invalid actions without explicitly defining what a valid action is and then prohibiting all others. I think it is important to do that, because, for example, we don’t want to allow a transfer without a receive logic being checked. How do you exclude certain behaviours (especially if they can take different forms) without excluding all behaviours that are not explicitly the valid ones?

You are also right about the intent application being independent from the kudos application, but the current design doesn’t actually prevent “the other” approach, this one just offers a different way to enforce authorship specifically.

Yes, unless the logic constraints it. The kudos logic itself is not aware of solvers, so an extra measure would help.

This makes sense, but this also implies the user computes the proofs associated with the resource. I can imagine that in practice, since the user already reveals some information, they might as well delegate the proof computation to solvers since it is also expensive. Generally revealing the witness shouldn’t be enough to steal a resource, logics must prevent it.

1 Like

If the issuer’s pk is neither in the label nor constrained in the KL, how do we ensure the correct pk is used for issuance? I’ll wait for the details in DL, which may provide the answer?

It actually involves two transfers assuming mutual trust, and each action is already balanced. This differs from the simple example in Specs.

I’m afraid defining them explicitly could be annoying due to the varying number of resources. For example, I sometimes need to change one resource into three, four, or ten resources. Listing all potential numbers is tricky. The key point is that KL must see all counterparts, as @kike mentioned.

Proper creation and consumption rules in resource logic should work. If the receive logic involves general operations such as transfer or swap, it can be triggered by default when creating a kudo resource, similar to sender_logic/receiver_logic in the Taiga protocol. Ideally, the receive logic could be tied to ownership (pk) in the value, allowing owners to decide whether to trigger it. If we don’t need to trigger the receive logic for a created resource sometimes, set a dummy one and the dummy proof generation and verification can even be skipped.

Yes, I assumed all proofs in one action are generated by a single actor, with no delegations or collaborations. While proof delegation, collaboration, aggregation, and function privacy are interesting, I believe they may not be as easy to implement in the short term as we thought, at least from my perspective. Long-term, it’s definitely promising and worth exploring. I suggest we start with proposals separately rather than applying these features to current applications if that makes sense.

Wait, it can be in the label! Just verified not by KL, but by DL. I’ll give you some examples to hopefully clarify what I mean:

So Alice wants to create AliceToken. She puts her pk in the label and decides that only her can issue more tokens, and puts the check in the DL.

Bob wants to create AnyoneToken that can be issued by anyone. The label doesn’t contain any pk and the DL doesn’t verify who issues the token.

Charlie wants to create a CharlieToken such that anyone can create a CharlieToken for Charlie but not anyone else. He puts his pk in the label. The DL doesn’t verify who issues the token but verifies that the pk from the label is also the assigned owner of the issued token.

You are right. Let’s imagine the swap example then. I consume my appleKudos, check the appleDL, and create an intent (signed by me) that states that I’d like to receive some orangeKudos in exchange for my apple kudos. This is one action. You do a symmetric thing with your orangeKudos, that is the second action. The solver creates the third action, creating an orangeKudo for me, appleKudo for you, and consuming our intents, including the relevant DL proofs. This exactly follows the example from the specs

Just so that we don’t forget it in the future: as we discussed on the cryptography call, the current plan is to explore how to make operations work for equivalence classes of kudos.

Ah, but that implies the owners being online, which we don’t want. It is a trade-off between the receiver being online and authorising the receive manually (by enforcing the logic check or not) or being completely offline and in that case the logic is checked each time

It definitely makes sense to me. I agree that these features should also be application-independent. But we should start exploring them regardless (not in the context of this application)

Yes, the swap follows the example with an intent in the specs. But I’m referring to the simple example without an intent. 1. Do we need to support it? 2. Can it be realized with the current design?

What are the operations for equivalence classes of kudos? I want to confirm my understanding. You defined 1-1 transfer and will define 1-2 change and 2-1 merge. My questions are: 1. Do we need to support m-n (arbitrary numbers) transfers, changes, and merges in one transaction(even in one action)? 2. Can this be achieved with the current design?

Yes, it should be in the label if the application wants to enable the issuance. I asked because you only included the dl_vk in the label. Although the pk and issuance are not verified in KL, the pk is still constrained and must be provided as witnesses since the label may represent a hash or finite field and we need to decode and verify the label in both KL and DL. According to your examples, you will make the pk and issuance optional. It would be helpful to clarify the label structure including the optional subfields, which was my original request.

So when the user creates the desired resource for themselves? You are right, in the current form it isn’t supported but it should be. I’ll modify the design to account for that. I’ll try to reformulate the constraints in a way that the logic doesn’t verify the balancing resource and see if it breaks any initial invariants. I’m sure it will work out.

This cannot be achieved with the current design in a neat way, I’ll change the design. What should result from the design update is that it doesn’t matter if there is one resource of quantity 5 or n resources of total quantity 5. I think I have an idea of how to do that.

This is what should be in the label for every DL. For the DL that constraint issuer identities, the issuer pk must be included too. I’ll clarify it in the DL description.

1 Like

KL constraints update

This update is meant to fix a number of issues with the current design:

  • equivalent classes members are not treated equally
  • the action structure is very limited and prohibits desired use cases

Base kudo structure

  • label: encodes the denomination logic verifying key. Can include additional parameters defined by the DL such as the issuer’s identity
  • value: encodes the owner’s identity

Consume

  1. The DL is triggered: there is a resource R_{DL} in the same action s.t. R_DL.logic = self.label.dl_vk

Create

  1. The DL is triggered: there is a resource R_{DL} in the same action s.t. R_DL.logic = self.label.dl_vk
  2. The receive logic is triggered[1]:
    1. There is a created resource R_{receive} in the transaction s.t.: Signature.Verify(Sig,R_receive.logic, self.value.owner) = True

Consequences:

  • We drop most of the KL constraints.
  • We no longer encode transaction functions in the logic and only constraint creation and consumption.
  • All equivalence classes properties hold[2]
  • I believe we still can get away with one DL trigger per denomination per action. Number of DL resources in the action = number of kudo denominations in the action. I’ll modify the DL logic accordingly and see if it works.
  • We can no longer have a verifiable intent mechanics, but that is okay because it only means that the user cannot delegate creating the initial transaction anymore.
  • We need to validate creating ephemeral kudos (e.g., with receive logic). More thoughts on this below

Validating created ephemeral kudos

What is the role of ephemeral resources in the kudos application? They are used exclusively for balancing. At the same time, if users are allowed to send ephemeral resources to other users, we must trigger their receive logic. If we don’t do that, it is still possible for users to “spam” other users with ephemeral kudos. At the same time, triggering receive logic for ephemeral kudos has no value and is costly. Sending ephemeral kudos shouldn’t be allowed.

The problem here is that we can’t just fail a transaction that contains ephemeral kudos since we do need them for balancing. My proposal is to prohibit sending ephemeral resources to anyone but the owner of the balancing consumed resources. This removes the incentive to spam since the user can only spam themselves. To enforce that, we must have the balancing consumed resource in the same transaction (which is somewhat limiting).

What if there are multiple consumed resources with different owners that correspond to such a created ephemeral resource? I don’t have a good answer for that question. Remember that the only situation when we need to create ephemeral kudos is burning, which is a somewhat special use case.

  • Do we want to allow burning multiple kudos of different owners at once?
  • Perhaps we can then call the receive logic for the group identity combining the owners of consumed resources (how to work with such an identity is out of scope of this post)

What I see as the best solution is to somehow bind the created ephemeral kudos to the creator of the kudo to remove the incentive.

Another idea I had is to remove ownership concept from ephemeral resources but this unfortunately doesn’t help since the resources themselves still are sent to someone. We need to make sure that someone can either control what they receive or the creator doesn’t have the incentive to spam the possible receiver.

Anyway, curious to hear what you think.

Example

Alice has some appleKudos and wants some orangeKudos. Bob has some orangeKudos and wants some appleKudos. They swap their resources without intents.

Alice’s action:

  • consume appleKudos
  • create orangeKudos
  • create e-appleDL (e- means ephemeral)
  • create e-orangeDL
  • create e-AliceReceive

Bob’s action:

  • create appleKudos
  • consume orangeKudos
  • create e-appleDL
  • create e-orangeDL
  • create e-BobReceive

What must be in the same action

  • Every action that contains a kudo of denomination D must contain a DL e-resource for D
  • Every action that contains a created kudo must contain a receive logic associated with the kudo’s owner

  1. if we find another way to constrain sending ephemeral kudos, this constrain can be checked for persistent resources only ↩︎

  2. ignoring the created ephemeral resources issue, depending on how we solve that ↩︎

1 Like