Shielded Kudos Revised (no authorisation abstraction)

This version doesn’t incorporate the authorisation abstraction for KL signature constraint yet

Kudos is an application that defines kudos denomination interface and describes how different denominations work. Kudos denominations might have some meaning assigned to them (e.g., money, trust, etc), but we will ignore the meanings here.

This is a revised version of the kudos application, it aggregates the results of the discussions from these posts:

High-level description

This kudos application allows creating kudos of multiple denominations. Kudos can be issued, burned, conditionally transferred, or swapped for other denominations or resources of unrelated kinds. Denominations are created dynamically and can contain arbitrary constraints. The kudo application logic ensures that the basic and denomination constraints are enforced.

Components

1. Kudo

This application operates on kudos. Kudos are resources that are characterised by the following resource fields:

  • kudo.logic: the main kudos logic (KL)
  • kudo.label: encodes a denomination logic (DL). Can include additional parameters expected by the DL such as the issuer’s identity
  • kudo.value: encodes the owner’s identity

Currently, identities are represented by public keys.

2. The main kudos logic KL

The main kudo logic KL verifies some universal constraints and ensures that the relevant logics such as DL and receive logic are verified, when required.

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

3. Denomination logics DL

The kudo application expects to have multiple kudos denominations. Each denomination has its own logic that is carried by an ephemeral resource. Each kudo resource specifies its denomination by encoding the relevant denomination logic. The KL then verifies that the DL of each kudo resource is verified in the same action.

A denomination might set additional constraints on the kudo resource of the corresponding denomination, for example, require what certain fields must contain.

Denomination logic itself is carried by an ephemeral resource.

DL example

This denomination logic authorises all actions by verifying signatures. The issuance/burn is authorised by the denomination owner, transfer is authorised by the kudo owner.

The kudo additional structure specified by this DL:
  • kudo.label: includes the issuer’s pk
  • kudo.value: includes the owner’s pk
Consumed kudo r
  • Ephemeral:
    • verify a signature issued by the denomination owner signing the list of cms + nfs of the action: Signature.Verify(r.issuer_pk, cms + nfs, Sig) = True
  • Persistent:
    • verify a signature issued by the kudo owner signing the list of cms + nfs of the action: Signature.Verify(r.owner_pk, cms + nfs, Sig) = True
Created kudo r
  • Ephemeral:
    • verify a signature issued by the denomination owner signing the list of cms + nfs of the action: Signature.Verify(r.issuer_pk, cms + nfs, Sig) = True

Proving system inputs

  • Instance: commitments, nullifiers (nothing extra)
  • Witness: resource objects (includes issuer’s pk), signatures

4. Receive logics

Receive logics are associated with users and describe when sending to this user is allowed. Receive logics must be authorised by the user they belong to and are triggered when a transfer is initiated. Receive logics are carried by ephemeral resources of the receive kind. Their verification is currently triggered by KL.

Non-trivial receive logic example

This receive logic only allows receiving tokens of non-zero quantity of the listed assets.

Receive resource (RLtrigger) fields:
  • RLtrigger.label: includes the identity of the user it belongs to
  • RLtrigger.logic: receive logic
Constraints:

There is a created kudo ‘r’ in the same action s.t.:

  • r.owner = self.label.owner (checking that the kudo is created for the person whose receive logic is currently being checked)
  • r.kind() is in allowedKinds. List of allowed kinds is hardcoded in the logic in this example. We can imagine that it is something like this: allowedKinds = [apple, orange, banana] (the list contains specific kinds instead of letters)
  • r.quantity > 0

The constraint is triggered for all receive logic resources (we only create ephemeral ones to trigger the logic by design, but we can just not check it since it doesn’t seem to allow any invalid behaviour)

Transaction functions

Issue and burn kudos

We start with issuing and burning kudos since they are pretty similar to each other and are the simplest action possible.

  • On the diagram, the grey resources correspond to kudos resources of denomination #n and the green resources represent the denomination #n authorisation logic.
  • KL requires a resource with a DL from the label to be present in the same action. The DL resource verifies the issuance conditions specific to that denomination.
  • The same idea works for burning, except that now we consume a persistent resource instead of creating a persistent resource.
  • The denomination resource is ephemeral and has zero value, we only need to trigger the logic verification.

Transferring kudos with conditional receive

To allow specifying receive policies, we need to require a receive logic to be verified. This implies we need a resource that carries that logic. It cannot be a part of DL since it isn’t associated with the denomination, it is associated with the receiver. Receive logic presence requirement must be a part of the kudos logic (i.e., required for all denominations), otherwise the malicious sender can always create a denomination that doesn’t require it and send it to the desired receiver.

  • As in the issue/burn case, we need to trigger the corresponding DL
  • To bind the receive authorisation logic to the action, KL requires that there is a resource in the same action that is associated with the specified receiver’s pubkey
  • To make sure the authorisation logic is indeed created by the receiver, the receive authorisation resource must contain a signature over the authorisation logic. This signature must be verified by KL
  • As for DL, receive resource only triggers the logic and therefore can be ephemeral and zero value

Swap

Swapping kudos (to kudos of other denominations or completely different resources) is not limited by the application design and works the same way as swapping any resources work.

Further work

Receive logic distribution and safety

The user is expected to share the signed version of their receive logic with whoever is going to send them the assets. It raises a few questions:

  • how to preserve the privacy of the receive logics and only reveal it to relevant parties / only reveal relevant parts of the logic
  • how to update logics and how to control which versions are used
  • how can multiple logics be maintained

We discussed these questions here and a bit here. For now we assume the simplest version: receive logics are distributed as needed, creating a new receive logic doesn’t invalidate the old one.

Validating created ephemeral kudos

The main discussion is here

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.

Other open questions


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

3 Likes

In KL, do we need to ensure the correct targeted DL and receive logic are triggered instead of just checking for existence? Otherwise, if there are multiple KLs in one action but only one DL is triggered, the action remains valid. Each KL expects a corresponding DL, so we need to verify the binding or reference. Did I overlook any constraints you may have checked elsewhere?

In the DL example, the issuer’s pk and owner’s pk come from DL’s label and value. To ensure these pks match those used in KL (resource), we may need a mechanism since KL only triggers DL without constraining its data (DL.label and DL.value). The denomination resource is ephemeral and has zero quantity, making DL.label and DL.value vulnerable to be maliciously replaced. A potential solution is for DL to load the kudo resource, retrieve the pks and other additional data from kudo_resource.label and kudo_resource.value, and verify their correspondence by just putting the kudo resource tag to the denomination label. This can also help simplify the label of denomination/receive resource make the denomination/receive resource unique.

I’d like to confirm the number of resources involved in each transaction function. For example, in the issue function, one persistent kudo resource is created and one ephemeral kudo resource is consumed. The persistent kudo triggers one DL and one receive logic, while the ephemeral kudo triggers one DL. This totals to five resources for issuance. Is my understanding correct? Some resources are omitted from the diagram, or I may have misunderstood something?

Yes. So the check in the KL technically verifies it is the correct denomination (we are comparing the verifying key hashes written in the resource label and the DL resource logic), but I’m not sure how we can check if we have one DL per kudo if we have kudos of the same denomination.

If one DL check is enough per action, it would be fine, but I’m not sure if one DL can handle all kudos of that denomination in the action.

To bind a DL to each kudo, we could encode something from the relevant kudo in the DL carrier, then we need to make sure that for each kudo there is a DL carrier that is bound to that kudo.

It all is relevant for the same denomination context though since different denominations have different logics.

We probably should also add some randomness to the denomination identifier. Currently it is assumed to be identified by the logic, but in case we want distinct denominations with the same logic, we need some randomness.

The label and value specified in the example are not fields of the DL carrying resource, they are fields of the kudos required by the DL to be in the kudo. I’ll update the constraints to reflect what resource the values come from.

Yes, that is currently correct, but I wonder if we can get away with one DL trigger per all kudos of the same denomination. I’m not sure we can, but would be cool.

1 Like

Yes, that’s what I proposed in the authorization post: putting the kudo resource tag to denomination label to enable mutual referencing between kudo and denomination resources. This also applies to receive resources. Does it make sense?

Right, theoretically we can, but practically it costs too much. Circuits are fixed and require a worst-case check, so we must enumerate the maximum resource numbers in the DL.

There’s one more thing we didn’t think about.
Where the kudo ciphertext should be located—in the instance of kudo, denomination, or receive?
What needs to be encrypted?
Where do we get the receiver’s pk for encryption? We could reuse the consumption pk here, but that might not be the best solution in the long run.

Kudos:

  1. How to bind the DL/RL trigger resource to the kudo that is being checked: encode the kudo tag in the DL/RL label (doesn’t interfere with the denomination kind)
  2. Associate the kudo ciphertext with the kudo resource; for DL/RL we might want to have dummy ciphertexts, although they make things even slower. In case we don’t have FP, we might not care - need to investigate a little bit
  3. For now assume we use the identity pk, but we need to have a proper key schedule mechanism
  4. Include ALL of the resources included in the action (one DL per kudo, one RL per receiver, balancing resources, etc)

Note: RL and DL have opposite consume/create statuses (in the risc0 implementation) to match them in the compliance unit and optimise

Other questions:

  1. Find a fixed position for the ciphertext field (in the appdata)

Proposal: Convert the denomination resource to the denomination constraint function and invoke the denomination constraint function within the kudo main logic.

The kudo main logic and denomination logic (as part of the kudo resource label) together determine the kudo kind. Currently, each kudo main resource requires a separate denomination resource, nearly doubling the total resources. I propose triggering denomination constraints directly within the kudo main logic. We can design an abstract denomination constraint function API. Developers would only need to design the denomination constraint function and pass it to the kudo main logic. This approach maintains the current kudo main and kudo denomination layout while reducing the resource number.

Note that this does not apply to the receive logic; invoking receive constraints in the kudo main logic would impact the kudo kind, which is undesirable

How would you pass the constraint to the kudo logic? In the witness?

yes, kind of. The witness generally refers to the data or variables used in circuits. We can pass the denomination-related witness(actually could be an object with arbitrary data and a constraint method) to the kudo logic, and the kudo main logic would enforce the denomination constraint method.

Wouldn’t it be insanely huge for an input?

Is it just for optimising the number of resources?

It’s not a big deal for the input. In fact, the method doesn’t take up any space in the input at all.